Skip to content

Fix invalid SQL from index TABLESPACE clause on partitioned tables - #10342

Open
dpage wants to merge 1 commit into
pgadmin-org:masterfrom
dpage:fix/issue-10341-partition-index-tablespace
Open

Fix invalid SQL from index TABLESPACE clause on partitioned tables#10342
dpage wants to merge 1 commit into
pgadmin-org:masterfrom
dpage:fix/issue-10341-partition-index-tablespace

Conversation

@dpage

@dpage dpage commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • PostgreSQL rejects an explicit TABLESPACE clause naming the database's own default tablespace when the index belongs to a partitioned table (cannot specify default tablespace for partitioned relations), so the SQL pgAdmin generated for a new index on a partitioned table could not be executed whenever the Tablespace field was left at its default (pg_default).
  • The index_constraint and exclusion_constraint create.sql templates already guard against emitting this redundant clause (data.spcname != "pg_default"); this extends the same guard to the plain index create.sql templates (default and 15_plus buckets).
  • Updated all affected fixture files under indexes/tests/{default,15_plus} to match the corrected output.

Test plan

  • Added test_create_sql_partitioned_tablespace.py, a DB-free unit test rendering the templates directly and asserting the redundant clause is omitted while an explicit non-default tablespace is still emitted.
  • regression/runtests.py --pkg browser.server_groups.servers.databases.schemas.tables.indexes.tests — 49/49 passing.
  • regression/runtests.py --pkg browser.server_groups.servers.databases.schemas.tables.tests — 113/113 passing (no regression in the parent Tables SQL, which embeds indexes).
  • Verified against a live PostgreSQL 18 server that CREATE INDEX ... TABLESPACE pg_default on a partitioned table errors, while a real non-default tablespace works fine.

Closes #10341

PostgreSQL rejects an explicit TABLESPACE clause naming the database's
own default tablespace when the index is on a partitioned table
('cannot specify default tablespace for partitioned relations'), so
the SQL pgAdmin generated for a new index could not be executed
against a partitioned table whenever no non-default tablespace was
chosen. The index_constraint and exclusion_constraint create.sql
templates already guard against emitting this redundant clause;
extend the same guard to the plain index create.sql templates.

Closes pgadmin-org#10341
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 52 minutes.

View limit details

Limit details: You’ve used all 8 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c10ead4e-8485-4127-9691-ab03583d9927

📥 Commits

Reviewing files that changed from the base of the PR and between bc58657 and 62813eb.

📒 Files selected for processing (35)
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/alter_index_no_depends.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/alter_name_fillfactor_comment.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/alter_reset_fillfactor_cluster.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_btree_asc_null_first.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_btree_asc_null_first_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_btree_asc_null_last.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_btree_asc_null_last_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_btree_desc_null_first.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_btree_desc_null_first_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_btree_desc_null_last.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_btree_desc_null_last_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_index_with_ext.msql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/15_plus/create_index_with_ext.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/alter_expr_statistics.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/alter_index_no_depends.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/alter_name_fillfactor_comment.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/alter_reset_fillfactor_cluster.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_asc_null_first.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_asc_null_first_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_asc_null_last.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_asc_null_last_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_desc_null_first.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_desc_null_first_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_desc_null_last.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_desc_null_last_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_expr_asc_null_last.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_btree_expr_asc_null_last_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_hash_index.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_hash_index_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_index_with_ext.msql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_index_with_ext.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/default/create_unnamed_hash_index_msql.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/tests/test_create_sql_partitioned_tablespace.py
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/indexes/sql/15_plus/create.sql
  • web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/indexes/sql/default/create.sql

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@asheshv

asheshv commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

The bug is real, but I think this fix makes things worse in one common configuration, so please don't merge it yet.

The core problem: pg_default isn't the same thing as "the database's default tablespace".

indexes/sql/default/properties.sql:13-24 derives spcname from pg_database.dattablespace — that is, the name of whatever tablespace the database defaults to, which is pg_default only in the common case. PostgreSQL's own check in DefineIndex/DefineRelation is tablespaceId == MyDatabaseTableSpace — also the database default, not the literal pg_default. This PR hardcodes the string pg_default, so the two disagree the moment a database was created with CREATE DATABASE x TABLESPACE ts_foo.

Two consequences, both bad:

1. Silent wrong tablespace placement (indexes/sql/default/create.sql:23, indexes/sql/15_plus/create.sql:29) — this is the serious one.
With the database default at ts_foo, an index the user deliberately put in pg_default stores reltablespace = 1663, so properties.sql reports spcname = 'pg_default' and the guard suppresses the clause. The reverse-engineered SQL (and Schema Diff) then recreates that index in ts_foo instead. Data silently lands in the wrong tablespace — strictly worse than the visible error this PR set out to fix.

2. In that same setup, the original bug isn't fixed.
An index inheriting the ts_foo default reports spcname = 'ts_foo', the clause is still emitted, and CREATE INDEX ... TABLESPACE ts_foo on a partitioned table still fails with cannot specify default tablespace for partitioned relations.

Both fall out of the same fix: compare against the database's actual default tablespace rather than the literal string. properties.sql already computes the value — surface it into the render context (e.g. db_default_tablespace) and guard on data.spcname != db_default_tablespace.

Also still broken: partitioned tables (tables/sql/default/create.sql:131-132 and the 16_plus sibling).
Identical unguarded {% if data.spcname %}TABLESPACE ..., and tests/pg/default/create_table_with_range_partition.sql:10,19 bakes in PARTITION BY RANGE (arr); ... TABLESPACE pg_default; — which PostgreSQL rejects for exactly the same reason. Creating a partitioned table is the more common path than a partitioned index, so either extend the fix or scope the PR explicitly and file a follow-up.

The suppression isn't limited to partitioned relations. It strips the clause from ordinary indexes' reverse-engineered SQL too. There's no partitioned indicator in the index template context today (only data.indisonly), so a targeted fix needs a bit of backend plumbing. If that's not worth it, please at least state the tradeoff in the commit message rather than leaving it implicit.

Cosmetic regression baked into the fixtures (32 files). Removing the clause leaves a stray blank line between WITH (...) and WHERE, and in the no-WHERE case a bare ; on its own line (e.g. indexes/tests/default/create_btree_asc_null_last.sql:10, create_hash_index.sql:8). Rather than updating fixtures to match, fix the whitespace with {%- ... -%} trims around the block.

The new test doesn't test what it looks like it tests (test_create_sql_partitioned_tablespace.py:47-51). It installs its own FileSystemLoader, bypassing VersionedTemplateLoader, so it proves nothing about version-bucket selection — which matters here, since there's no indexes/sql/16_plus/create.sql and PG 16-17 fall through to 15_plus (correct, but untested). It also only renders mode='create', never the reverse-engineered path, and has no case for a non-pg_default database default — the very scenario that breaks. (FWIW I checked the _FakeApp / get_driver trick for test pollution: DriverRegistry.get is memoized and neither driver defines init_app, so it's a harmless no-op.)

No injection concern — data.spcname still goes through conn|qtIdent and this is a comparison change only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generated SQL for indexes invalid for partitioned tables

2 participants