From f6c6a63756ad51b338fdc5561d60736a1117ca9d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 31 Aug 2026 11:44:31 +0000 Subject: [PATCH 1/7] Skip redundant pgvector extension creation Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- MEVD/src/PgVector/PostgresCollection.cs | 33 ++++++++++++------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/MEVD/src/PgVector/PostgresCollection.cs b/MEVD/src/PgVector/PostgresCollection.cs index 96025fe..c3ed312 100644 --- a/MEVD/src/PgVector/PostgresCollection.cs +++ b/MEVD/src/PgVector/PostgresCollection.cs @@ -494,28 +494,27 @@ private async Task InternalCreateCollectionAsync(bool ifNotExists, CancellationT using var batch = connection.CreateBatch(); // First, check if the pgvector extension is already installed in PostgreSQL, and then install it if not. - // Note that we do a separate check before doing CREATE EXTENSION IF EXISTS in order to know if it was actually created, - // since in that case we must also must call ReloadTypesAsync() at the Npgsql level + // The check must be executed separately so that users without permission to create extensions can use an + // extension that was installed by an administrator. batch.BatchCommands.Add(new NpgsqlBatchCommand("SELECT EXISTS(SELECT * FROM pg_extension WHERE extname='vector')")); - batch.BatchCommands.Add(new NpgsqlBatchCommand("CREATE EXTENSION IF NOT EXISTS vector")); - bool extensionAlreadyExisted; - - try - { - extensionAlreadyExisted = (bool)(await batch.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false))!; - } - catch (PostgresException e) when (e.SqlState == PostgresErrorCodes.UniqueViolation) - { - // CREATE EXTENSION IF NOT EXISTS is not atomic in PG, so concurrent sessions doing this at the same time - // may trigger a unique constraint violation. We ignore it and interpret it to mean that the extension - // already exists. - extensionAlreadyExisted = true; - } + bool extensionAlreadyExisted = (bool)(await batch.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false))!; if (!extensionAlreadyExisted) { - await connection.ReloadTypesAsync().ConfigureAwait(false); + batch.BatchCommands.Clear(); + batch.BatchCommands.Add(new NpgsqlBatchCommand("CREATE EXTENSION IF NOT EXISTS vector")); + + try + { + await batch.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); + await connection.ReloadTypesAsync().ConfigureAwait(false); + } + catch (PostgresException e) when (e.SqlState == PostgresErrorCodes.UniqueViolation) + { + // CREATE EXTENSION IF NOT EXISTS is not atomic in PG, so concurrent sessions doing this at the same time + // may trigger a unique constraint violation. We ignore it since the extension now exists. + } } batch.BatchCommands.Clear(); From 4c25d6f365f438114950cf551c1596c39523d126 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 31 Aug 2026 11:46:36 +0000 Subject: [PATCH 2/7] Reload vector types after concurrent extension creation Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- MEVD/src/PgVector/PostgresCollection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MEVD/src/PgVector/PostgresCollection.cs b/MEVD/src/PgVector/PostgresCollection.cs index c3ed312..4748c95 100644 --- a/MEVD/src/PgVector/PostgresCollection.cs +++ b/MEVD/src/PgVector/PostgresCollection.cs @@ -514,6 +514,7 @@ private async Task InternalCreateCollectionAsync(bool ifNotExists, CancellationT { // CREATE EXTENSION IF NOT EXISTS is not atomic in PG, so concurrent sessions doing this at the same time // may trigger a unique constraint violation. We ignore it since the extension now exists. + await connection.ReloadTypesAsync().ConfigureAwait(false); } } From 7a7fa23070358c6f50526bbc2317a28f4acc9517 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 31 Aug 2026 11:59:48 +0000 Subject: [PATCH 3/7] Bump PgVector package version to 1.0.1 Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- MEVD/src/PgVector/PgVector.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MEVD/src/PgVector/PgVector.csproj b/MEVD/src/PgVector/PgVector.csproj index 1478620..2d9e8ab 100644 --- a/MEVD/src/PgVector/PgVector.csproj +++ b/MEVD/src/PgVector/PgVector.csproj @@ -1,7 +1,7 @@  - 1.0.0 + 1.0.1 CommunityToolkit.VectorData.PgVector $(AssemblyName) net10.0;net8.0;netstandard2.0;net462 From 5b55610fdf540e4881d3f09796d7f697684613da Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 31 Aug 2026 12:02:49 +0000 Subject: [PATCH 4/7] Handle pgvector creation race with helper check Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- MEVD/src/PgVector/PostgresCollection.cs | 26 +++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/MEVD/src/PgVector/PostgresCollection.cs b/MEVD/src/PgVector/PostgresCollection.cs index 4748c95..714b089 100644 --- a/MEVD/src/PgVector/PostgresCollection.cs +++ b/MEVD/src/PgVector/PostgresCollection.cs @@ -488,7 +488,7 @@ private async Task InternalCreateCollectionAsync(bool ifNotExists, CancellationT await using (connection) { - var pgVersion = connection.PostgreSqlVersion; + Version pgVersion = connection.PostgreSqlVersion; // Prepare the SQL commands. using var batch = connection.CreateBatch(); @@ -496,9 +496,7 @@ private async Task InternalCreateCollectionAsync(bool ifNotExists, CancellationT // First, check if the pgvector extension is already installed in PostgreSQL, and then install it if not. // The check must be executed separately so that users without permission to create extensions can use an // extension that was installed by an administrator. - batch.BatchCommands.Add(new NpgsqlBatchCommand("SELECT EXISTS(SELECT * FROM pg_extension WHERE extname='vector')")); - - bool extensionAlreadyExisted = (bool)(await batch.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false))!; + bool extensionAlreadyExisted = await IsVectorExtensionInstalledAsync(connection, cancellationToken).ConfigureAwait(false); if (!extensionAlreadyExisted) { @@ -516,6 +514,17 @@ private async Task InternalCreateCollectionAsync(bool ifNotExists, CancellationT // may trigger a unique constraint violation. We ignore it since the extension now exists. await connection.ReloadTypesAsync().ConfigureAwait(false); } + catch (PostgresException e) when (e.SqlState == PostgresErrorCodes.InsufficientPrivilege) + { + bool extensionInstalledConcurrently = await IsVectorExtensionInstalledAsync(connection, cancellationToken).ConfigureAwait(false); + + if (!extensionInstalledConcurrently) + { + throw; + } + + await connection.ReloadTypesAsync().ConfigureAwait(false); + } } batch.BatchCommands.Clear(); @@ -542,6 +551,15 @@ private Task RunOperationAsync(string operationName, Func operation) operationName, operation); + private static async Task IsVectorExtensionInstalledAsync(NpgsqlConnection connection, CancellationToken cancellationToken) + { + using NpgsqlCommand command = connection.CreateCommand(); + command.CommandText = "SELECT EXISTS(SELECT * FROM pg_extension WHERE extname='vector')"; + + bool extensionAlreadyExisted = (bool)(await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false))!; + return extensionAlreadyExisted; + } + private Task RunOperationAsync(string operationName, Func> operation) => VectorStoreErrorHandler.RunOperationAsync( _collectionMetadata, From 486ba1891b8e4d11aedd11fbf641ecf096d09552 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 31 Aug 2026 12:03:33 +0000 Subject: [PATCH 5/7] Simplify extension helper return path Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- MEVD/src/PgVector/PostgresCollection.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/MEVD/src/PgVector/PostgresCollection.cs b/MEVD/src/PgVector/PostgresCollection.cs index 714b089..b77a4b9 100644 --- a/MEVD/src/PgVector/PostgresCollection.cs +++ b/MEVD/src/PgVector/PostgresCollection.cs @@ -556,8 +556,7 @@ private static async Task IsVectorExtensionInstalledAsync(NpgsqlConnection using NpgsqlCommand command = connection.CreateCommand(); command.CommandText = "SELECT EXISTS(SELECT * FROM pg_extension WHERE extname='vector')"; - bool extensionAlreadyExisted = (bool)(await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false))!; - return extensionAlreadyExisted; + return (bool)(await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false))!; } private Task RunOperationAsync(string operationName, Func> operation) From d7040949e89fd3b64611a8391204044ed065a1ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 31 Aug 2026 12:04:12 +0000 Subject: [PATCH 6/7] Use SELECT 1 for extension existence check Co-authored-by: adamsitnik <6011991+adamsitnik@users.noreply.github.com> --- MEVD/src/PgVector/PostgresCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MEVD/src/PgVector/PostgresCollection.cs b/MEVD/src/PgVector/PostgresCollection.cs index b77a4b9..1f8d5fa 100644 --- a/MEVD/src/PgVector/PostgresCollection.cs +++ b/MEVD/src/PgVector/PostgresCollection.cs @@ -554,7 +554,7 @@ private Task RunOperationAsync(string operationName, Func operation) private static async Task IsVectorExtensionInstalledAsync(NpgsqlConnection connection, CancellationToken cancellationToken) { using NpgsqlCommand command = connection.CreateCommand(); - command.CommandText = "SELECT EXISTS(SELECT * FROM pg_extension WHERE extname='vector')"; + command.CommandText = "SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname='vector')"; return (bool)(await command.ExecuteScalarAsync(cancellationToken).ConfigureAwait(false))!; } From 897d76563282ecfdc0eb70be4b787e80edbea61e Mon Sep 17 00:00:00 2001 From: Adam Sitnik Date: Mon, 31 Aug 2026 14:08:53 +0200 Subject: [PATCH 7/7] reduce code duplication --- MEVD/src/PgVector/PostgresCollection.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/MEVD/src/PgVector/PostgresCollection.cs b/MEVD/src/PgVector/PostgresCollection.cs index 1f8d5fa..7c05e93 100644 --- a/MEVD/src/PgVector/PostgresCollection.cs +++ b/MEVD/src/PgVector/PostgresCollection.cs @@ -506,25 +506,22 @@ private async Task InternalCreateCollectionAsync(bool ifNotExists, CancellationT try { await batch.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); - await connection.ReloadTypesAsync().ConfigureAwait(false); } catch (PostgresException e) when (e.SqlState == PostgresErrorCodes.UniqueViolation) { // CREATE EXTENSION IF NOT EXISTS is not atomic in PG, so concurrent sessions doing this at the same time // may trigger a unique constraint violation. We ignore it since the extension now exists. - await connection.ReloadTypesAsync().ConfigureAwait(false); } catch (PostgresException e) when (e.SqlState == PostgresErrorCodes.InsufficientPrivilege) { bool extensionInstalledConcurrently = await IsVectorExtensionInstalledAsync(connection, cancellationToken).ConfigureAwait(false); - if (!extensionInstalledConcurrently) { throw; } - - await connection.ReloadTypesAsync().ConfigureAwait(false); } + + await connection.ReloadTypesAsync().ConfigureAwait(false); } batch.BatchCommands.Clear();