From 82094987ced885cabda42ecf22a55dee42268caa Mon Sep 17 00:00:00 2001 From: Nikolay Samokhvalov Date: Mon, 29 Sep 2025 20:27:17 -0700 Subject: [PATCH 1/4] fix: resolve operator ambiguity in i3 foreign key check Add explicit int2[] cast to key_cols parameter to resolve "operator is not unique" error when using the @> operator with pg_catalog schema qualification. Fixes #62 --- sql/i3_non_indexed_fks.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/i3_non_indexed_fks.sql b/sql/i3_non_indexed_fks.sql index 1b448e6..6667976 100644 --- a/sql/i3_non_indexed_fks.sql +++ b/sql/i3_non_indexed_fks.sql @@ -63,7 +63,7 @@ with fk_actions ( code, action ) as ( join fk_cols_list using (fkoid) left join index_list on conrelid = indrelid - and (indkey::int2[])[0:(array_length(key_cols,1) -1)] operator(pg_catalog.@>) key_cols + and (indkey::int2[])[0:(array_length(key_cols,1) -1)] operator(pg_catalog.@>) key_cols::int2[] ), fk_perfect_match as ( select fkoid From 8d0217545f960c018aad5d07fcb36e4626bfc62b Mon Sep 17 00:00:00 2001 From: Nikolay Samokhvalov Date: Mon, 29 Sep 2025 20:34:44 -0700 Subject: [PATCH 2/4] test(ci): add comprehensive test for i3 foreign key check Add test coverage for i3_non_indexed_fks.sql query to catch potential operator ambiguity issues. The test: - Installs intarray extension which can cause operator ambiguity - Creates parent and child tables with foreign keys - Verifies i3 query executes without errors - Checks that foreign keys without indexes are detected Also update all psql commands in CI to use PAGER=cat per PostgresAI command execution rules to ensure non-interactive behavior. Related to #62 --- .github/workflows/test.yml | 52 +++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0ac7c29..e1312aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -58,6 +58,7 @@ jobs: # Create extensions (pg_stat_statements may not work without shared_preload_libraries) psql -h localhost -U postgres -d test -c 'CREATE EXTENSION IF NOT EXISTS pg_stat_statements;' || echo "Warning: pg_stat_statements extension not available" psql -h localhost -U postgres -d test -c 'CREATE EXTENSION IF NOT EXISTS pgstattuple;' + psql -h localhost -U postgres -d test -c 'CREATE EXTENSION IF NOT EXISTS intarray;' # Create minimal privilege user for testing psql -h localhost -U postgres -d test -c "CREATE USER dba_user;" @@ -71,7 +72,14 @@ jobs: # Create test tables for alignment testing (as superuser) psql -h localhost -U postgres -d test -c "CREATE TABLE align1 AS SELECT 1::int4, 2::int8, 3::int4 AS more FROM generate_series(1, 100000) _(i);" psql -h localhost -U postgres -d test -c "CREATE TABLE align2 AS SELECT 1::int4, 3::int4 AS more, 2::int8 FROM generate_series(1, 100000) _(i);" - + + # Create test tables for foreign key testing (i3 query) + psql -h localhost -U postgres -d test -c "CREATE TABLE fk_parent (id int PRIMARY KEY, data text);" + psql -h localhost -U postgres -d test -c "CREATE TABLE fk_child (id int PRIMARY KEY, parent_id int, data text, CONSTRAINT fk_test FOREIGN KEY (parent_id) REFERENCES fk_parent(id));" + psql -h localhost -U postgres -d test -c "INSERT INTO fk_parent SELECT i, 'data_' || i FROM generate_series(1, 100000) i;" + psql -h localhost -U postgres -d test -c "INSERT INTO fk_child SELECT i, (i % 100000) + 1, 'data_' || i FROM generate_series(1, 200000) i;" + psql -h localhost -U postgres -d test -c "ANALYZE;" + # Grant access to test tables for dba_user psql -h localhost -U postgres -d test -c "GRANT SELECT ON ALL TABLES IN SCHEMA public TO dba_user;" @@ -83,12 +91,12 @@ jobs: echo "\set postgres_dba_wide true" > ~/.psqlrc echo "\set postgres_dba_interactive_mode false" >> ~/.psqlrc echo "Testing all SQL files in wide mode with minimal privileges..." - for f in sql/*; do + for f in sql/*; do echo " Testing $f..." - if ! psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f "$f" > /dev/null 2>&1; then + if ! PAGER=cat psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f "$f" > /dev/null 2>&1; then echo "❌ FAILED: $f in wide mode" echo "Error output:" - psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f "$f" + PAGER=cat psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f "$f" exit 1 fi done @@ -99,12 +107,12 @@ jobs: echo "\set postgres_dba_wide false" > ~/.psqlrc echo "\set postgres_dba_interactive_mode false" >> ~/.psqlrc echo "Testing all SQL files in normal mode with minimal privileges..." - for f in sql/*; do + for f in sql/*; do echo " Testing $f..." - if ! psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f "$f" > /dev/null 2>&1; then + if ! PAGER=cat psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f "$f" > /dev/null 2>&1; then echo "❌ FAILED: $f in normal mode" echo "Error output:" - psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f "$f" + PAGER=cat psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f "$f" exit 1 fi done @@ -114,36 +122,50 @@ jobs: run: | echo "\set postgres_dba_wide false" > ~/.psqlrc echo "\set postgres_dba_interactive_mode false" >> ~/.psqlrc - + echo "Running regression tests with minimal privileges..." - + echo " Testing 0_node.sql..." - OUTPUT=$(psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f sql/0_node.sql | grep Role) + OUTPUT=$(PAGER=cat psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f sql/0_node.sql | grep Role) if [[ "$OUTPUT" == *"Master"* ]]; then echo " ✓ Role test passed" else echo " ✗ Role test failed: $OUTPUT" exit 1 fi - + echo " Testing p1_alignment_padding.sql..." - OUTPUT=$(psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f sql/p1_alignment_padding.sql | grep align) + OUTPUT=$(PAGER=cat psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f sql/p1_alignment_padding.sql | grep align) if [[ "$OUTPUT" == *"align1"* && "$OUTPUT" == *"align2"* && "$OUTPUT" == *"int4, more, int8"* ]]; then echo " ✓ Alignment padding test passed" else echo " ✗ Alignment padding test failed: $OUTPUT" exit 1 fi - + echo " Testing a1_activity.sql..." - OUTPUT=$(psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f sql/a1_activity.sql | grep User) + OUTPUT=$(PAGER=cat psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f sql/a1_activity.sql | grep User) if [[ "$OUTPUT" == *"User"* ]]; then echo " ✓ Activity test passed" else echo " ✗ Activity test failed: $OUTPUT" exit 1 fi - + + echo " Testing i3_non_indexed_fks.sql (with intarray extension)..." + OUTPUT=$(PAGER=cat psql -h localhost -U dba_user -d test --no-psqlrc -f warmup.psql -f sql/i3_non_indexed_fks.sql 2>&1) + if [[ "$OUTPUT" == *"ERROR"* ]]; then + echo " ✗ i3 test failed with error:" + echo "$OUTPUT" + exit 1 + elif [[ "$OUTPUT" == *"fk_child"* && "$OUTPUT" == *"fk_test"* ]]; then + echo " ✓ i3 foreign key test passed (found foreign key without index)" + else + echo " ✗ i3 test failed: unexpected output" + echo "$OUTPUT" + exit 1 + fi + echo "✅ All regression tests passed with minimal privileges" From d9f648e68399796b15c92e48e81006981ed1c652 Mon Sep 17 00:00:00 2001 From: Nikolay Samokhvalov Date: Mon, 29 Sep 2025 20:35:04 -0700 Subject: [PATCH 3/4] revert: remove type cast to demonstrate CI test failure Temporarily remove the ::int2[] cast on key_cols parameter to verify that the new CI test correctly catches the operator ambiguity error. This commit is expected to fail CI tests. Related to #62 --- sql/i3_non_indexed_fks.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/i3_non_indexed_fks.sql b/sql/i3_non_indexed_fks.sql index 6667976..1b448e6 100644 --- a/sql/i3_non_indexed_fks.sql +++ b/sql/i3_non_indexed_fks.sql @@ -63,7 +63,7 @@ with fk_actions ( code, action ) as ( join fk_cols_list using (fkoid) left join index_list on conrelid = indrelid - and (indkey::int2[])[0:(array_length(key_cols,1) -1)] operator(pg_catalog.@>) key_cols::int2[] + and (indkey::int2[])[0:(array_length(key_cols,1) -1)] operator(pg_catalog.@>) key_cols ), fk_perfect_match as ( select fkoid From 97b7863bf2df8371f10ea911d216028e32b3527b Mon Sep 17 00:00:00 2001 From: Nikolay Samokhvalov Date: Mon, 29 Sep 2025 20:37:01 -0700 Subject: [PATCH 4/4] fix: add explicit type cast to resolve operator ambiguity in i3 Add explicit `::int2[]` cast to `key_cols` parameter in the i3 foreign key check query. This resolves "operator is not unique" errors that can occur in certain PostgreSQL environments (such as Google CloudSQL). The error occurs when PostgreSQL cannot determine which @> operator to use between smallint[] operands. By explicitly casting both sides to int2[], we eliminate any ambiguity. While standard PostgreSQL installations (including those with intarray extension) may not exhibit this error, the explicit cast is a best practice that ensures consistent behavior across all environments. Fixes #62 --- sql/i3_non_indexed_fks.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/i3_non_indexed_fks.sql b/sql/i3_non_indexed_fks.sql index 1b448e6..6667976 100644 --- a/sql/i3_non_indexed_fks.sql +++ b/sql/i3_non_indexed_fks.sql @@ -63,7 +63,7 @@ with fk_actions ( code, action ) as ( join fk_cols_list using (fkoid) left join index_list on conrelid = indrelid - and (indkey::int2[])[0:(array_length(key_cols,1) -1)] operator(pg_catalog.@>) key_cols + and (indkey::int2[])[0:(array_length(key_cols,1) -1)] operator(pg_catalog.@>) key_cols::int2[] ), fk_perfect_match as ( select fkoid