From ed7a1d514f8b769d88efd7775beb3714bfb2283f Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Sun, 12 Apr 2026 07:40:14 -0400 Subject: [PATCH 1/5] Fix SQLPrimaryKeys to exclude INCLUDE columns from results When a PRIMARY KEY is created with an INCLUDE clause (PostgreSQL 11+), SQLPrimaryKeys incorrectly returned both the key columns and the included columns. For example, given PRIMARY KEY (a, b) INCLUDE (c, d), all four columns were returned instead of just a and b. The root cause was that the pg_attribute join on the index relation iterated over all index attributes without distinguishing key columns from included columns. Fix by adding a filter on i.indnkeyatts when connected to PostgreSQL >= 11, which limits results to only the actual key attributes. Both query paths in PGAPI_PrimaryKeys are fixed. Add a regression test that creates a table with a composite primary key using INCLUDE and verifies only the key columns are returned. --- info.c | 14 ++++-- test/expected/primarykeys-include.out | 6 +++ test/src/primarykeys-include-test.c | 65 +++++++++++++++++++++++++++ test/tests | 3 +- 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 test/expected/primarykeys-include.out create mode 100644 test/src/primarykeys-include-test.c diff --git a/info.c b/info.c index 755baed7..23623442 100644 --- a/info.c +++ b/info.c @@ -4055,7 +4055,11 @@ PGAPI_PrimaryKeys(HSTMT hstmt, " AND ta.attnum operator(pg_catalog.=) i.indkey[ia.attnum-1]" " AND (NOT ta.attisdropped)" " AND (NOT ia.attisdropped)" - " AND ic.oid operator(pg_catalog.=) i.indexrelid" + " AND ic.oid operator(pg_catalog.=) i.indexrelid"); + if (PG_VERSION_GE(conn, 11.0)) + appendPQExpBufferStr(&tables_query, + " AND ia.attnum operator(pg_catalog.<=) i.indnkeyatts"); + appendPQExpBufferStr(&tables_query, " order by ia.attnum"); break; case 2: @@ -4075,8 +4079,12 @@ PGAPI_PrimaryKeys(HSTMT hstmt, " AND ta.attrelid operator(pg_catalog.=) i.indrelid" " AND ta.attnum operator(pg_catalog.=) i.indkey[ia.attnum-1]" " AND (NOT ta.attisdropped)" - " AND (NOT ia.attisdropped)" - " order by ia.attnum", eq_string, escTableName, eq_string, pkscm); + " AND (NOT ia.attisdropped)", eq_string, escTableName, eq_string, pkscm); + if (PG_VERSION_GE(conn, 11.0)) + appendPQExpBufferStr(&tables_query, + " AND ia.attnum operator(pg_catalog.<=) i.indnkeyatts"); + appendPQExpBufferStr(&tables_query, + " order by ia.attnum"); break; } if (PQExpBufferDataBroken(tables_query)) diff --git a/test/expected/primarykeys-include.out b/test/expected/primarykeys-include.out new file mode 100644 index 00000000..03d2769b --- /dev/null +++ b/test/expected/primarykeys-include.out @@ -0,0 +1,6 @@ +connected +Check SQLPrimaryKeys with INCLUDE columns +Result set: +contrib_regression public pk_include_test a 1 pk_include_test_pkey +contrib_regression public pk_include_test b 2 pk_include_test_pkey +disconnecting diff --git a/test/src/primarykeys-include-test.c b/test/src/primarykeys-include-test.c new file mode 100644 index 00000000..1ad704f8 --- /dev/null +++ b/test/src/primarykeys-include-test.c @@ -0,0 +1,65 @@ +/* + * Test that SQLPrimaryKeys excludes INCLUDE columns. + * + * A PRIMARY KEY with INCLUDE (PG 11+) should only return the actual + * key columns, not the included columns. + */ +#include +#include + +#include "common.h" + +int +main(int argc, char **argv) +{ + SQLRETURN rc; + HSTMT hstmt = SQL_NULL_HSTMT; + + test_connect(); + + rc = SQLAllocHandle(SQL_HANDLE_STMT, conn, &hstmt); + CHECK_CONN_RESULT(rc, "failed to allocate stmt handle", conn); + + /* INCLUDE clause requires PG >= 11 */ + if (server_version_lt(conn, 11, 0)) + { + printf("Server version < 11, skipping INCLUDE test\n"); + test_disconnect(); + return 0; + } + + /* Create table with PK that has INCLUDE columns */ + rc = SQLExecDirect(hstmt, + (SQLCHAR *) "DROP TABLE IF EXISTS pk_include_test", SQL_NTS); + CHECK_STMT_RESULT(rc, "DROP TABLE failed", hstmt); + + rc = SQLExecDirect(hstmt, + (SQLCHAR *) "CREATE TABLE pk_include_test (a int, b int, c int, d int," + " CONSTRAINT pk_include_test_pkey PRIMARY KEY (a, b) INCLUDE (c, d))", + SQL_NTS); + CHECK_STMT_RESULT(rc, "CREATE TABLE failed", hstmt); + + rc = SQLFreeStmt(hstmt, SQL_CLOSE); + CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt); + + /* Call SQLPrimaryKeys - should return only a and b, not c or d */ + printf("Check SQLPrimaryKeys with INCLUDE columns\n"); + rc = SQLPrimaryKeys(hstmt, + NULL, 0, + (SQLCHAR *) "public", SQL_NTS, + (SQLCHAR *) "pk_include_test", SQL_NTS); + CHECK_STMT_RESULT(rc, "SQLPrimaryKeys failed", hstmt); + print_result(hstmt); + + rc = SQLFreeStmt(hstmt, SQL_CLOSE); + CHECK_STMT_RESULT(rc, "SQLFreeStmt failed", hstmt); + + /* Clean up */ + rc = SQLExecDirect(hstmt, + (SQLCHAR *) "DROP TABLE pk_include_test", SQL_NTS); + CHECK_STMT_RESULT(rc, "DROP TABLE failed", hstmt); + + test_disconnect(); + + return 0; +} diff --git a/test/tests b/test/tests index ead21ea6..d7cbec08 100644 --- a/test/tests +++ b/test/tests @@ -57,4 +57,5 @@ TESTBINS = exe/connect-test \ exe/wchar-char-test \ exe/params-batch-exec-test \ exe/fetch-refcursors-test \ - exe/descrec-test + exe/descrec-test \ + exe/primarykeys-include-test From 99aa975927d28ba343099459657dae089b762073 Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Sun, 12 Apr 2026 18:15:10 -0700 Subject: [PATCH 2/5] use openssl 3.5.6 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 31bad56e..d6a339d8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,7 +16,7 @@ env: # PostgreSQL build from source. POSTGRESQL_SOURCE_TAG: 'REL_18_STABLE' - OPENSSL_VERSION: '3_5_5' + OPENSSL_VERSION: '3_5_6' PKGCONFIGLITE_VERSION: '0.28-1' WINFLEXBISON_VERSION: '2.5.24' WORKFLOW_VERSION_POSTGRESQL: '1' # increment to invalidate cache From ece26f9d780cc4764a586abd2c2641d40d63deff Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Mon, 13 Apr 2026 04:31:45 -0700 Subject: [PATCH 3/5] accept wix v7 eula --- installer/buildInstallers.ps1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/installer/buildInstallers.ps1 b/installer/buildInstallers.ps1 index 7ba24ba8..670d88d0 100644 --- a/installer/buildInstallers.ps1 +++ b/installer/buildInstallers.ps1 @@ -46,6 +46,9 @@ Param( [String]$str_msvcp="msvcp" [String]$msrun_ptn="msvcr|vcruntime" +# Accept WiX v7 OSMF EULA +wix eula accept wix7 2>$null + function msvcrun([int]$runtime_version) { [String]$str = if ($runtime_version -lt $ucrt_version) {$str_msvcr} else {$str_vcrun} From c78dc7ebb5c046802367aa5b6a887740e92217c8 Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Mon, 13 Apr 2026 06:37:17 -0700 Subject: [PATCH 4/5] accept the wix eula before running any wix commands --- .github/workflows/main.yml | 1 + installer/buildInstallers.ps1 | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d6a339d8..d299cbf2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -323,6 +323,7 @@ jobs: shell: cmd run: | dotnet tool install --global wix + wix eula accept wix7 wix extension add --global WixToolset.UI.wixext/6.0.0 - name: Build psqlodbc ${{matrix.variant}} diff --git a/installer/buildInstallers.ps1 b/installer/buildInstallers.ps1 index 670d88d0..7ba24ba8 100644 --- a/installer/buildInstallers.ps1 +++ b/installer/buildInstallers.ps1 @@ -46,9 +46,6 @@ Param( [String]$str_msvcp="msvcp" [String]$msrun_ptn="msvcr|vcruntime" -# Accept WiX v7 OSMF EULA -wix eula accept wix7 2>$null - function msvcrun([int]$runtime_version) { [String]$str = if ($runtime_version -lt $ucrt_version) {$str_msvcr} else {$str_vcrun} From c0c87aa00c05076d23b6cb4f144cfbd8481d000a Mon Sep 17 00:00:00 2001 From: Dave Cramer Date: Mon, 13 Apr 2026 09:13:47 -0700 Subject: [PATCH 5/5] Install wix version 6.0 --- .github/workflows/main.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d299cbf2..a3d2f712 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -322,8 +322,7 @@ jobs: - name: Install WiX shell: cmd run: | - dotnet tool install --global wix - wix eula accept wix7 + dotnet tool install --global wix --version "6.*" wix extension add --global WixToolset.UI.wixext/6.0.0 - name: Build psqlodbc ${{matrix.variant}}