Skip to content

Commit 3775756

Browse files
committed
[MEVD] Make PG schema nullable
Closes #11355
1 parent ea864bf commit 3775756

File tree

6 files changed

+260
-69
lines changed

6 files changed

+260
-69
lines changed

dotnet/src/VectorData/PgVector/PostgresCollection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class PostgresCollection<TKey, TRecord> : VectorStoreCollection<TKey, TRe
4141
private readonly string _databaseName;
4242

4343
/// <summary>The database schema.</summary>
44-
private readonly string _schema;
44+
private readonly string? _schema;
4545

4646
/// <summary>The model for this collection.</summary>
4747
private readonly CollectionModel _model;

dotnet/src/VectorData/PgVector/PostgresCollectionOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ public PostgresCollectionOptions()
2020

2121
internal PostgresCollectionOptions(PostgresCollectionOptions? source) : base(source)
2222
{
23-
this.Schema = source?.Schema ?? PostgresVectorStoreOptions.Default.Schema;
23+
this.Schema = source?.Schema;
2424
}
2525

2626
/// <summary>
2727
/// Gets or sets the database schema.
2828
/// </summary>
29-
public string Schema { get; set; } = PostgresVectorStoreOptions.Default.Schema;
29+
public string? Schema { get; set; }
3030
}

dotnet/src/VectorData/PgVector/PostgresSqlBuilder.cs

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -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.")]
2121
internal 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

dotnet/src/VectorData/PgVector/PostgresVectorStore.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public sealed class PostgresVectorStore : VectorStore
3030
private readonly string _databaseName;
3131

3232
/// <summary>The database schema.</summary>
33-
private readonly string _schema;
33+
private readonly string? _schema;
3434

3535
private readonly IEmbeddingGenerator? _embeddingGenerator;
3636

@@ -44,7 +44,7 @@ public PostgresVectorStore(NpgsqlDataSource dataSource, bool ownsDataSource, Pos
4444
{
4545
Verify.NotNull(dataSource);
4646

47-
this._schema = options?.Schema ?? PostgresVectorStoreOptions.Default.Schema;
47+
this._schema = options?.Schema;
4848
this._embeddingGenerator = options?.EmbeddingGenerator;
4949
this._dataSource = dataSource;
5050
this._dataSourceArc = ownsDataSource ? new NpgsqlDataSourceArc(dataSource) : null;

dotnet/src/VectorData/PgVector/PostgresVectorStoreOptions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ public PostgresVectorStoreOptions()
2020

2121
internal PostgresVectorStoreOptions(PostgresVectorStoreOptions? source)
2222
{
23-
this.Schema = source?.Schema ?? Default.Schema;
23+
this.Schema = source?.Schema;
2424
this.EmbeddingGenerator = source?.EmbeddingGenerator;
2525
}
2626

2727
/// <summary>
2828
/// Gets or sets the database schema.
2929
/// </summary>
30-
public string Schema { get; set; } = "public";
30+
public string? Schema { get; set; }
3131

3232
/// <summary>
3333
/// Gets or sets the default embedding generator to use when generating vectors embeddings with this vector store.

0 commit comments

Comments
 (0)