@@ -916,7 +916,7 @@ async fn test_execute_statement(test_db: PgPool) -> Result<()> {
916916        . find_map ( |action_or_cmd| match  action_or_cmd { 
917917            lsp:: CodeActionOrCommand :: CodeAction ( code_action)  => { 
918918                let  command = code_action. command . as_ref ( ) ; 
919-                 if  command. is_some_and ( |cmd| & cmd. command  == "pgt .executeStatement" )  { 
919+                 if  command. is_some_and ( |cmd| & cmd. command  == "pgls .executeStatement" )  { 
920920                    let  command = command. unwrap ( ) ; 
921921                    let  arguments = command. arguments . as_ref ( ) . unwrap ( ) . clone ( ) ; 
922922                    Some ( ( command. command . clone ( ) ,  arguments) ) 
@@ -952,6 +952,160 @@ async fn test_execute_statement(test_db: PgPool) -> Result<()> {
952952    Ok ( ( ) ) 
953953} 
954954
955+ #[ sqlx:: test( migrator = "pgls_test_utils::MIGRATIONS" ) ]  
956+ async  fn  test_invalidate_schema_cache ( test_db :  PgPool )  -> Result < ( ) >  { 
957+     let  factory = ServerFactory :: default ( ) ; 
958+     let  mut  fs = MemoryFileSystem :: default ( ) ; 
959+ 
960+     let  database = test_db
961+         . connect_options ( ) 
962+         . get_database ( ) 
963+         . unwrap ( ) 
964+         . to_string ( ) ; 
965+     let  host = test_db. connect_options ( ) . get_host ( ) . to_string ( ) ; 
966+ 
967+     // Setup: Create a table with only id column (no name column yet) 
968+     let  setup = r#" 
969+         create table public.users ( 
970+             id serial primary key 
971+         ); 
972+     "# ; 
973+ 
974+     test_db
975+         . execute ( setup) 
976+         . await 
977+         . expect ( "Failed to setup test database" ) ; 
978+ 
979+     let  mut  conf = PartialConfiguration :: init ( ) ; 
980+     conf. merge_with ( PartialConfiguration  { 
981+         db :  Some ( PartialDatabaseConfiguration  { 
982+             database :  Some ( database) , 
983+             host :  Some ( host) , 
984+             ..Default :: default ( ) 
985+         } ) , 
986+         ..Default :: default ( ) 
987+     } ) ; 
988+ 
989+     fs. insert ( 
990+         url ! ( "postgres-language-server.jsonc" ) 
991+             . to_file_path ( ) 
992+             . unwrap ( ) , 
993+         serde_json:: to_string_pretty ( & conf) . unwrap ( ) , 
994+     ) ; 
995+ 
996+     let  ( service,  client)  = factory
997+         . create_with_fs ( None ,  DynRef :: Owned ( Box :: new ( fs) ) ) 
998+         . into_inner ( ) ; 
999+ 
1000+     let  ( stream,  sink)  = client. split ( ) ; 
1001+     let  mut  server = Server :: new ( service) ; 
1002+ 
1003+     let  ( sender,  mut  receiver)  = channel ( CHANNEL_BUFFER_SIZE ) ; 
1004+     let  reader = tokio:: spawn ( client_handler ( stream,  sink,  sender) ) ; 
1005+ 
1006+     server. initialize ( ) . await ?; 
1007+     server. initialized ( ) . await ?; 
1008+ 
1009+     server. load_configuration ( ) . await ?; 
1010+ 
1011+     // Open a document that references a non-existent 'name' column 
1012+     let  doc_content = "select name from public.users;" ; 
1013+     server. open_document ( doc_content) . await ?; 
1014+ 
1015+     // Wait for typecheck diagnostics showing column doesn't exist 
1016+     let  got_error = tokio:: time:: timeout ( Duration :: from_secs ( 5 ) ,  async  { 
1017+         loop  { 
1018+             match  receiver. next ( ) . await  { 
1019+                 Some ( ServerNotification :: PublishDiagnostics ( msg) )  => { 
1020+                     if  msg
1021+                         . diagnostics 
1022+                         . iter ( ) 
1023+                         . any ( |d| d. message . contains ( "column \" name\"  does not exist" ) ) 
1024+                     { 
1025+                         return  true ; 
1026+                     } 
1027+                 } 
1028+                 _ => continue , 
1029+             } 
1030+         } 
1031+     } ) 
1032+     . await 
1033+     . is_ok ( ) ; 
1034+ 
1035+     assert ! ( 
1036+         got_error, 
1037+         "Expected typecheck error for non-existent column 'name'" 
1038+     ) ; 
1039+ 
1040+     // Add the missing column to the database 
1041+     let  alter_table = r#" 
1042+         alter table public.users 
1043+         add column name text; 
1044+     "# ; 
1045+ 
1046+     test_db
1047+         . execute ( alter_table) 
1048+         . await 
1049+         . expect ( "Failed to add column to table" ) ; 
1050+ 
1051+     // Invalidate the schema cache (all = false for current connection only) 
1052+     server
1053+         . request :: < bool ,  ( ) > ( "pgt/invalidate_schema_cache" ,  "_invalidate_cache" ,  false ) 
1054+         . await ?; 
1055+ 
1056+     // Change the document slightly to trigger re-analysis 
1057+     server
1058+         . change_document ( 
1059+             1 , 
1060+             vec ! [ TextDocumentContentChangeEvent  { 
1061+                 range:  Some ( Range  { 
1062+                     start:  Position  { 
1063+                         line:  0 , 
1064+                         character:  30 , 
1065+                     } , 
1066+                     end:  Position  { 
1067+                         line:  0 , 
1068+                         character:  30 , 
1069+                     } , 
1070+                 } ) , 
1071+                 range_length:  Some ( 0 ) , 
1072+                 text:  " " . to_string( ) , 
1073+             } ] , 
1074+         ) 
1075+         . await ?; 
1076+ 
1077+     // Wait for diagnostics to clear (no typecheck error anymore) 
1078+     let  error_cleared = tokio:: time:: timeout ( Duration :: from_secs ( 5 ) ,  async  { 
1079+         loop  { 
1080+             match  receiver. next ( ) . await  { 
1081+                 Some ( ServerNotification :: PublishDiagnostics ( msg) )  => { 
1082+                     // Check that there's no typecheck error for the column 
1083+                     let  has_column_error = msg
1084+                         . diagnostics 
1085+                         . iter ( ) 
1086+                         . any ( |d| d. message . contains ( "column \" name\"  does not exist" ) ) ; 
1087+                     if  !has_column_error { 
1088+                         return  true ; 
1089+                     } 
1090+                 } 
1091+                 _ => continue , 
1092+             } 
1093+         } 
1094+     } ) 
1095+     . await 
1096+     . is_ok ( ) ; 
1097+ 
1098+     assert ! ( 
1099+         error_cleared, 
1100+         "Expected typecheck error to be cleared after schema cache invalidation" 
1101+     ) ; 
1102+ 
1103+     server. shutdown ( ) . await ?; 
1104+     reader. abort ( ) ; 
1105+ 
1106+     Ok ( ( ) ) 
1107+ } 
1108+ 
9551109#[ sqlx:: test( migrator = "pgls_test_utils::MIGRATIONS" ) ]  
9561110async  fn  test_issue_281 ( test_db :  PgPool )  -> Result < ( ) >  { 
9571111    let  factory = ServerFactory :: default ( ) ; 
0 commit comments