Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions cmd/dump/dump_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,74 @@ func TestDumpCommand_Issue307ViewDependencyOrder(t *testing.T) {
runExactMatchTest(t, "issue_307_view_dependency_order")
}

func TestDumpCommand_Issue318CrossSchemaComment(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
}

// Setup PostgreSQL
embeddedPG := testutil.SetupPostgres(t)
defer embeddedPG.Stop()

// Connect to database
conn, host, port, dbname, user, password := testutil.ConnectToPostgres(t, embeddedPG)
defer conn.Close()

// Read and execute the setup SQL that creates two schemas with same-named tables
setupPath := "../../testdata/dump/issue_318_cross_schema_comment/setup.sql"
setupContent, err := os.ReadFile(setupPath)
if err != nil {
t.Fatalf("Failed to read %s: %v", setupPath, err)
}

_, err = conn.ExecContext(context.Background(), string(setupContent))
if err != nil {
t.Fatalf("Failed to execute setup.sql: %v", err)
}

// Dump each schema and verify comments are correctly attributed
tests := []struct {
schema string
tableComment string
colComment string
}{
{"alpha", "Alpha account table", "Alpha account name"},
{"beta", "Beta account table", "Beta account name"},
}

for _, tc := range tests {
t.Run(tc.schema, func(t *testing.T) {
config := &DumpConfig{
Host: host,
Port: port,
DB: dbname,
User: user,
Password: password,
Schema: tc.schema,
MultiFile: false,
File: "",
}

output, err := ExecuteDump(config)
if err != nil {
t.Fatalf("Dump command failed for schema %s: %v", tc.schema, err)
}

// Verify table comment
expectedTableComment := fmt.Sprintf("COMMENT ON TABLE account IS '%s';", tc.tableComment)
if !strings.Contains(output, expectedTableComment) {
t.Errorf("Schema %s: expected table comment %q not found in output:\n%s", tc.schema, expectedTableComment, output)
}

// Verify column comment
expectedColComment := fmt.Sprintf("COMMENT ON COLUMN account.name IS '%s';", tc.colComment)
if !strings.Contains(output, expectedColComment) {
t.Errorf("Schema %s: expected column comment %q not found in output:\n%s", tc.schema, expectedColComment, output)
}
})
}
}

func TestDumpCommand_Issue307MultiFileViewDependencyOrder(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration test in short mode")
Expand Down
22 changes: 11 additions & 11 deletions ir/queries/queries.sql
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ SELECT
t.table_type,
COALESCE(d.description, '') AS table_comment
FROM information_schema.tables t
LEFT JOIN pg_class c ON c.relname = t.table_name
LEFT JOIN pg_namespace n ON c.relnamespace = n.oid AND n.nspname = t.table_schema
LEFT JOIN pg_namespace n ON n.nspname = t.table_schema
LEFT JOIN pg_class c ON c.relname = t.table_name AND c.relnamespace = n.oid
LEFT JOIN pg_description d ON d.objoid = c.oid AND d.classoid = 'pg_class'::regclass AND d.objsubid = 0
WHERE
WHERE
t.table_schema NOT IN ('information_schema', 'pg_catalog', 'pg_toast')
AND t.table_schema NOT LIKE 'pg_temp_%'
AND t.table_schema NOT LIKE 'pg_toast_temp_%'
Expand All @@ -40,16 +40,16 @@ ORDER BY t.table_schema, t.table_name;

-- GetTablesForSchema retrieves all tables in a specific schema with metadata
-- name: GetTablesForSchema :many
SELECT
SELECT
t.table_schema,
t.table_name,
t.table_type,
COALESCE(d.description, '') AS table_comment
FROM information_schema.tables t
LEFT JOIN pg_class c ON c.relname = t.table_name
LEFT JOIN pg_namespace n ON c.relnamespace = n.oid AND n.nspname = t.table_schema
LEFT JOIN pg_namespace n ON n.nspname = t.table_schema
LEFT JOIN pg_class c ON c.relname = t.table_name AND c.relnamespace = n.oid
LEFT JOIN pg_description d ON d.objoid = c.oid AND d.classoid = 'pg_class'::regclass AND d.objsubid = 0
WHERE
WHERE
t.table_schema = $1
AND t.table_type IN ('BASE TABLE', 'VIEW')
ORDER BY t.table_name;
Expand Down Expand Up @@ -109,8 +109,8 @@ WITH column_base AS (
ad.adbin,
ad.adrelid
FROM information_schema.columns c
LEFT JOIN pg_class cl ON cl.relname = c.table_name
LEFT JOIN pg_namespace n ON cl.relnamespace = n.oid AND n.nspname = c.table_schema
LEFT JOIN pg_namespace n ON n.nspname = c.table_schema
LEFT JOIN pg_class cl ON cl.relname = c.table_name AND cl.relnamespace = n.oid
LEFT JOIN pg_description d ON d.objoid = cl.oid AND d.classoid = 'pg_class'::regclass AND d.objsubid = c.ordinal_position
LEFT JOIN pg_attribute a ON a.attrelid = cl.oid AND a.attname = c.column_name
LEFT JOIN pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum
Expand Down Expand Up @@ -939,8 +939,8 @@ SELECT
COALESCE(dep_table.relname, col_table.table_name) AS owned_by_table,
COALESCE(dep_col.attname, col_table.column_name) AS owned_by_column
FROM pg_sequences s
LEFT JOIN pg_class c ON c.relname = s.sequencename
LEFT JOIN pg_namespace n ON c.relnamespace = n.oid AND n.nspname = s.schemaname
LEFT JOIN pg_namespace n ON n.nspname = s.schemaname
LEFT JOIN pg_class c ON c.relname = s.sequencename AND c.relnamespace = n.oid
-- Method 1: Try to find dependency relationship (for proper SERIAL columns)
LEFT JOIN pg_depend d ON d.objid = c.oid AND d.classid = 'pg_class'::regclass AND d.deptype IN ('a', 'i')
LEFT JOIN pg_class dep_table ON d.refobjid = dep_table.oid
Expand Down
20 changes: 10 additions & 10 deletions ir/queries/queries.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions testdata/dump/issue_318_cross_schema_comment/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "issue_318_cross_schema_comment",
"description": "Test case for wrong table/column comments when same table name exists in multiple schemas (GitHub issue #318)",
"source": "https://github.com/pgplex/pgschema/issues/318",
"notes": [
"Reproduces the bug where pg_class join on relname alone (without relnamespace) causes wrong comment attribution",
"Tests that table and column comments are correctly scoped to their own schema when dumping"
]
}
24 changes: 24 additions & 0 deletions testdata/dump/issue_318_cross_schema_comment/setup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--
-- Setup: Two schemas with identically-named tables but different comments.
-- This reproduces GitHub issue #318 where the buggy pg_class join on relname
-- alone (without relnamespace) can cause wrong comment attribution.
--

CREATE SCHEMA alpha;
CREATE SCHEMA beta;

CREATE TABLE alpha.account (
id serial PRIMARY KEY,
name text NOT NULL
);

COMMENT ON TABLE alpha.account IS 'Alpha account table';
COMMENT ON COLUMN alpha.account.name IS 'Alpha account name';

CREATE TABLE beta.account (
id serial PRIMARY KEY,
name text NOT NULL
);

COMMENT ON TABLE beta.account IS 'Beta account table';
COMMENT ON COLUMN beta.account.name IS 'Beta account name';