@@ -20,33 +20,36 @@ namespace Microsoft.SemanticKernel.Connectors.PgVector;
2020[ System . Diagnostics . CodeAnalysis . SuppressMessage ( "Security" , "CA2100:Review SQL queries for security vulnerabilities" , Justification = "We need to build the full table name using schema and collection, it does not support parameterized passing." ) ]
2121internal static class PostgresSqlBuilder
2222{
23- internal static void BuildDoesTableExistCommand ( NpgsqlCommand command , string schema , string tableName )
23+ internal static void BuildDoesTableExistCommand ( NpgsqlCommand command , string ? schema , string tableName )
2424 {
25- command . CommandText = """
26- SELECT table_name
27- FROM information_schema.tables
28- WHERE table_schema = $1
29- AND table_type = 'BASE TABLE'
30- AND table_name = $2
31- """ ;
32-
3325 Debug . Assert ( command . Parameters . Count == 0 ) ;
34- command . Parameters . Add ( new ( ) { Value = schema } ) ;
26+
27+ command . CommandText = """
28+ SELECT table_name
29+ FROM information_schema.tables
30+ WHERE table_schema = $1
31+ AND table_type = 'BASE TABLE'
32+ AND table_name = $2
33+ """ ;
34+
35+ command . Parameters . Add ( new ( ) { Value = schema ?? "public" } ) ;
3536 command . Parameters . Add ( new ( ) { Value = tableName } ) ;
3637 }
3738
38- internal static void BuildGetTablesCommand ( NpgsqlCommand command , string schema )
39+ internal static void BuildGetTablesCommand ( NpgsqlCommand command , string ? schema )
3940 {
40- command . CommandText = """
41- SELECT table_name
42- FROM information_schema.tables
43- WHERE table_schema = $1 AND table_type = 'BASE TABLE'
44- """ ;
4541 Debug . Assert ( command . Parameters . Count == 0 ) ;
46- command . Parameters . Add ( new ( ) { Value = schema } ) ;
42+
43+ command . CommandText = """
44+ SELECT table_name
45+ FROM information_schema.tables
46+ WHERE table_schema = $1 AND table_type = 'BASE TABLE'
47+ """ ;
48+
49+ command . Parameters . Add ( new ( ) { Value = schema ?? "public" } ) ;
4750 }
4851
49- internal static string BuildCreateTableSql ( string schema , string tableName , CollectionModel model , Version pgVersion , bool ifNotExists = true )
52+ internal static string BuildCreateTableSql ( string ? schema , string tableName , CollectionModel model , Version pgVersion , bool ifNotExists = true )
5053 {
5154 if ( string . IsNullOrWhiteSpace ( tableName ) )
5255 {
@@ -61,7 +64,7 @@ internal static string BuildCreateTableSql(string schema, string tableName, Coll
6164 {
6265 createTableCommand . Append ( "IF NOT EXISTS " ) ;
6366 }
64- createTableCommand . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName ) . AppendLine ( " (" ) ;
67+ createTableCommand . AppendTableName ( schema , tableName ) . AppendLine ( " (" ) ;
6568
6669 // Add the key column
6770 var keyStoreType = PostgresPropertyMapping . GetPostgresTypeName ( model . KeyProperty ) . PgType ;
@@ -119,7 +122,7 @@ internal static string BuildCreateTableSql(string schema, string tableName, Coll
119122 }
120123
121124 /// <inheritdoc />
122- internal static string BuildCreateIndexSql ( string schema , string tableName , string columnName , string indexKind , string distanceFunction , bool isVector , bool isFullText , string ? fullTextLanguage , bool ifNotExists )
125+ internal static string BuildCreateIndexSql ( string ? schema , string tableName , string columnName , string indexKind , string distanceFunction , bool isVector , bool isFullText , string ? fullTextLanguage , bool ifNotExists )
123126 {
124127 var indexName = $ "{ tableName } _{ columnName } _index";
125128
@@ -134,14 +137,14 @@ internal static string BuildCreateIndexSql(string schema, string tableName, stri
134137 {
135138 // Create a GIN index for full-text search
136139 var language = fullTextLanguage ?? PostgresConstants . DefaultFullTextSearchLanguage ;
137- sql . AppendIdentifier ( indexName ) . Append ( " ON " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName )
140+ sql . AppendIdentifier ( indexName ) . Append ( " ON " ) . AppendTableName ( schema , tableName )
138141 . Append ( " USING GIN (to_tsvector(" ) . AppendLiteral ( language ) . Append ( ", " ) . AppendIdentifier ( columnName ) . Append ( "))" ) ;
139142 return sql . ToString ( ) ;
140143 }
141144
142145 if ( ! isVector )
143146 {
144- sql . AppendIdentifier ( indexName ) . Append ( " ON " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName )
147+ sql . AppendIdentifier ( indexName ) . Append ( " ON " ) . AppendTableName ( schema , tableName )
145148 . Append ( " (" ) . AppendIdentifier ( columnName ) . Append ( ')' ) ;
146149 return sql . ToString ( ) ;
147150 }
@@ -167,23 +170,23 @@ internal static string BuildCreateIndexSql(string schema, string tableName, stri
167170 _ => throw new NotSupportedException ( $ "Distance function { distanceFunction } is not supported.")
168171 } ;
169172
170- sql . AppendIdentifier ( indexName ) . Append ( " ON " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName )
173+ sql . AppendIdentifier ( indexName ) . Append ( " ON " ) . AppendTableName ( schema , tableName )
171174 . Append ( " USING " ) . Append ( indexTypeName ) . Append ( " (" ) . AppendIdentifier ( columnName ) . Append ( ' ' ) . Append ( indexOps ) . Append ( ')' ) ;
172175 return sql . ToString ( ) ;
173176 }
174177
175178 /// <inheritdoc />
176- internal static void BuildDropTableCommand ( NpgsqlCommand command , string schema , string tableName )
179+ internal static void BuildDropTableCommand ( NpgsqlCommand command , string ? schema , string tableName )
177180 {
178181 StringBuilder sql = new ( ) ;
179- sql . Append ( "DROP TABLE IF EXISTS " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName ) ;
182+ sql . Append ( "DROP TABLE IF EXISTS " ) . AppendTableName ( schema , tableName ) ;
180183 command . CommandText = sql . ToString ( ) ;
181184 }
182185
183186 /// <inheritdoc />
184187 internal static bool BuildUpsertCommand < TKey > (
185188 NpgsqlBatch batch ,
186- string schema ,
189+ string ? schema ,
187190 string tableName ,
188191 CollectionModel model ,
189192 IEnumerable < object > records ,
@@ -260,9 +263,7 @@ string GenerateSingleUpsertSql(bool autoGeneratedKey)
260263
261264 sqlBuilder
262265 . Append ( "INSERT INTO " )
263- . AppendIdentifier ( schema )
264- . Append ( '.' )
265- . AppendIdentifier ( tableName )
266+ . AppendTableName ( schema , tableName )
266267 . Append ( " (" ) ;
267268
268269 var i = 0 ;
@@ -345,7 +346,7 @@ string GenerateSingleUpsertSql(bool autoGeneratedKey)
345346 }
346347
347348 /// <inheritdoc />
348- internal static void BuildGetCommand < TKey > ( NpgsqlCommand command , string schema , string tableName , CollectionModel model , TKey key , bool includeVectors = false )
349+ internal static void BuildGetCommand < TKey > ( NpgsqlCommand command , string ? schema , string tableName , CollectionModel model , TKey key , bool includeVectors = false )
349350 where TKey : notnull
350351 {
351352 StringBuilder sql = new ( ) ;
@@ -360,7 +361,7 @@ internal static void BuildGetCommand<TKey>(NpgsqlCommand command, string schema,
360361 sql . AppendIdentifier ( model . Properties [ i ] . StorageName ) ;
361362 }
362363
363- sql . AppendLine ( ) . Append ( "FROM " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName ) . AppendLine ( )
364+ sql . AppendLine ( ) . Append ( "FROM " ) . AppendTableName ( schema , tableName ) . AppendLine ( )
364365 . Append ( "WHERE " ) . AppendIdentifier ( model . KeyProperty . StorageName ) . Append ( " = $1;" ) ;
365366
366367 command . CommandText = sql . ToString ( ) ;
@@ -369,7 +370,7 @@ internal static void BuildGetCommand<TKey>(NpgsqlCommand command, string schema,
369370 }
370371
371372 /// <inheritdoc />
372- internal static void BuildGetBatchCommand < TKey > ( NpgsqlCommand command , string schema , string tableName , CollectionModel model , List < TKey > keys , bool includeVectors = false )
373+ internal static void BuildGetBatchCommand < TKey > ( NpgsqlCommand command , string ? schema , string tableName , CollectionModel model , List < TKey > keys , bool includeVectors = false )
373374 where TKey : notnull
374375 {
375376 NpgsqlDbType ? keyType = PostgresPropertyMapping . GetNpgsqlDbType ( model . KeyProperty ) ?? throw new UnreachableException ( $ "Unsupported key type { model . KeyProperty . Type . Name } ") ;
@@ -393,7 +394,7 @@ internal static void BuildGetBatchCommand<TKey>(NpgsqlCommand command, string sc
393394 sql . AppendIdentifier ( property . StorageName ) ;
394395 }
395396
396- sql . AppendLine ( ) . Append ( "FROM " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName ) . AppendLine ( )
397+ sql . AppendLine ( ) . Append ( "FROM " ) . AppendTableName ( schema , tableName ) . AppendLine ( )
397398 . Append ( "WHERE " ) . AppendIdentifier ( model . KeyProperty . StorageName ) . Append ( " = ANY($1);" ) ;
398399
399400 command . CommandText = sql . ToString ( ) ;
@@ -406,10 +407,10 @@ internal static void BuildGetBatchCommand<TKey>(NpgsqlCommand command, string sc
406407 }
407408
408409 /// <inheritdoc />
409- internal static void BuildDeleteCommand < TKey > ( NpgsqlCommand command , string schema , string tableName , string keyColumn , TKey key )
410+ internal static void BuildDeleteCommand < TKey > ( NpgsqlCommand command , string ? schema , string tableName , string keyColumn , TKey key )
410411 {
411412 StringBuilder sql = new ( ) ;
412- sql . Append ( "DELETE FROM " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName ) . AppendLine ( )
413+ sql . Append ( "DELETE FROM " ) . AppendTableName ( schema , tableName ) . AppendLine ( )
413414 . Append ( "WHERE " ) . AppendIdentifier ( keyColumn ) . Append ( " = $1;" ) ;
414415
415416 command . CommandText = sql . ToString ( ) ;
@@ -418,7 +419,7 @@ internal static void BuildDeleteCommand<TKey>(NpgsqlCommand command, string sche
418419 }
419420
420421 /// <inheritdoc />
421- internal static void BuildDeleteBatchCommand < TKey > ( NpgsqlCommand command , string schema , string tableName , KeyPropertyModel keyProperty , List < TKey > keys )
422+ internal static void BuildDeleteBatchCommand < TKey > ( NpgsqlCommand command , string ? schema , string tableName , KeyPropertyModel keyProperty , List < TKey > keys )
422423 {
423424 NpgsqlDbType ? keyType = PostgresPropertyMapping . GetNpgsqlDbType ( keyProperty ) ?? throw new ArgumentException ( $ "Unsupported key type { typeof ( TKey ) . Name } ") ;
424425
@@ -431,7 +432,7 @@ internal static void BuildDeleteBatchCommand<TKey>(NpgsqlCommand command, string
431432 }
432433
433434 StringBuilder sql = new ( ) ;
434- sql . Append ( "DELETE FROM " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName ) . AppendLine ( )
435+ sql . Append ( "DELETE FROM " ) . AppendTableName ( schema , tableName ) . AppendLine ( )
435436 . Append ( "WHERE " ) . AppendIdentifier ( keyProperty . StorageName ) . Append ( " = ANY($1);" ) ;
436437
437438 command . CommandText = sql . ToString ( ) ;
@@ -504,7 +505,7 @@ private static (string Condition, List<object> Parameters) GenerateFilterConditi
504505#pragma warning disable CS0618 // VectorSearchFilter is obsolete
505506 /// <inheritdoc />
506507 internal static void BuildGetNearestMatchCommand < TRecord > (
507- NpgsqlCommand command , string schema , string tableName , CollectionModel model , VectorPropertyModel vectorProperty , object vectorValue ,
508+ NpgsqlCommand command , string ? schema , string tableName , CollectionModel model , VectorPropertyModel vectorProperty , object vectorValue ,
508509 VectorSearchFilter ? legacyFilter , Expression < Func < TRecord , bool > > ? newFilter , int ? skip , bool includeVectors , int limit ,
509510 double ? scoreThreshold = null )
510511 {
@@ -528,7 +529,7 @@ internal static void BuildGetNearestMatchCommand<TRecord>(
528529 StringBuilder sql = new ( ) ;
529530 sql . Append ( "SELECT " ) . Append ( columns ) . Append ( ", " ) . AppendIdentifier ( vectorProperty . StorageName )
530531 . Append ( ' ' ) . Append ( distanceOp ) . Append ( " $1 AS " ) . AppendIdentifier ( PostgresConstants . DistanceColumnName ) . AppendLine ( )
531- . Append ( "FROM " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName )
532+ . Append ( "FROM " ) . AppendTableName ( schema , tableName )
532533 . Append ( ' ' ) . AppendLine ( where )
533534 . Append ( "ORDER BY " ) . AppendLine ( PostgresConstants . DistanceColumnName )
534535 . Append ( "LIMIT " ) . Append ( limit ) ;
@@ -608,7 +609,7 @@ or DistanceFunction.HammingDistance
608609 }
609610
610611 internal static void BuildSelectWhereCommand < TRecord > (
611- NpgsqlCommand command , string schema , string tableName , CollectionModel model ,
612+ NpgsqlCommand command , string ? schema , string tableName , CollectionModel model ,
612613 Expression < Func < TRecord , bool > > filter , int top , FilteredRecordRetrievalOptions < TRecord > options )
613614 {
614615 StringBuilder query = new ( 200 ) ;
@@ -627,7 +628,7 @@ internal static void BuildSelectWhereCommand<TRecord>(
627628 }
628629 }
629630 query . AppendLine ( ) ;
630- query . Append ( "FROM " ) . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName ) . AppendLine ( ) ;
631+ query . Append ( "FROM " ) . AppendTableName ( schema , tableName ) . AppendLine ( ) ;
631632
632633 PostgresFilterTranslator translator = new ( model , filter , startParamIndex : 1 , query ) ;
633634 translator . Translate ( appendWhere : true ) ;
@@ -678,6 +679,19 @@ internal static (string Condition, List<object> Parameters) GenerateNewFilterCon
678679 return ( translator . Clause . ToString ( ) , translator . ParameterValues ) ;
679680 }
680681
682+ /// <summary>
683+ /// Appends a schema-qualified table name. If schema is null or empty, omits the schema prefix.
684+ /// </summary>
685+ private static StringBuilder AppendTableName ( this StringBuilder sb , string ? schema , string tableName )
686+ {
687+ if ( ! string . IsNullOrEmpty ( schema ) )
688+ {
689+ sb . AppendIdentifier ( schema ! ) . Append ( '.' ) ;
690+ }
691+
692+ return sb . AppendIdentifier ( tableName ) ;
693+ }
694+
681695#pragma warning disable CS0618 // VectorSearchFilter is obsolete
682696 internal static ( string Clause , List < object > Parameters ) GenerateLegacyFilterWhereClause ( CollectionModel model , VectorSearchFilter legacyFilter , int startParamIndex )
683697 {
@@ -739,7 +753,7 @@ internal static (string Condition, List<object> Parameters) GenerateLegacyFilter
739753 /// Builds a hybrid search command that combines vector similarity search with full-text keyword search using RRF (Reciprocal Rank Fusion).
740754 /// </summary>
741755 internal static void BuildHybridSearchCommand < TRecord > (
742- NpgsqlCommand command , string schema , string tableName , CollectionModel model ,
756+ NpgsqlCommand command , string ? schema , string tableName , CollectionModel model ,
743757 VectorPropertyModel vectorProperty , DataPropertyModel textProperty ,
744758 object vectorValue , ICollection < string > keywords ,
745759 VectorSearchFilter ? legacyFilter , Expression < Func < TRecord , bool > > ? newFilter ,
@@ -773,7 +787,7 @@ internal static void BuildHybridSearchCommand<TRecord>(
773787
774788 // Build the full table name
775789 var fullTableName = new StringBuilder ( )
776- . AppendIdentifier ( schema ) . Append ( '.' ) . AppendIdentifier ( tableName )
790+ . AppendTableName ( schema , tableName )
777791 . ToString ( ) ;
778792
779793 // Use a larger internal limit for the CTEs to get better ranking, then limit final results
0 commit comments