From 8f1ff4f41e3bf22e9ff008f23da69f7de7314a99 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Mon, 12 Oct 2026 15:15:01 +1100 Subject: [PATCH v8] Fix escapes for psql describe schema patterns psql \dn was building the query with a raw '%s' substitution of the user-supplied pattern, so a pattern containing a single quote (\dn "HAS ' QUOTE") broke out of the string literal and produced a syntax error on the server. The failure happened when querying to find publications involving this schema. Fixed by using a string escape function instead of raw-substitution to the SQL. Author: Peter Smith Reviewed-by: Steven Niu Reviewed by: Jim Jones Reviewed-by: Nishant Sharma Reviewed-by: surya poondla Reviewed-by: Ajin Cherian Discussion: https://www.postgresql.org/message-id/flat/SA5PPF230C70D4BFBD6006437E357AE7CF9A7CB2%40SA5PPF230C70D4B.namprd14.prod.outlook.com#c4b1b145852d2076db6590e0aae5c7da --- src/bin/psql/describe.c | 17 +++--- src/test/regress/expected/publication.out | 71 +++++++++++++++++++++++ src/test/regress/sql/publication.sql | 23 ++++++++ 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index dc9b3841592..9b2566ca8f4 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -5233,14 +5233,15 @@ listSchemas(const char *pattern, bool verbose, bool showSystem) printfPQExpBuffer(&buf, "/* %s */\n", _("Get publications that publish this schema")); - appendPQExpBuffer(&buf, - "SELECT pubname \n" - "FROM pg_catalog.pg_publication p\n" - " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n" - " JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n" - "WHERE n.nspname = '%s'\n" - "ORDER BY 1", - pattern); + appendPQExpBufferStr(&buf, + "SELECT pubname \n" + "FROM pg_catalog.pg_publication p\n" + " JOIN pg_catalog.pg_publication_namespace pn ON p.oid = pn.pnpubid\n" + " JOIN pg_catalog.pg_namespace n ON n.oid = pn.pnnspid \n" + "WHERE n.oid = pg_catalog.to_regnamespace("); + appendStringLiteralConn(&buf, pattern, pset.db); + appendPQExpBufferStr(&buf, ")\nORDER BY 1;"); + result = PSQLexec(buf.data); if (!result) goto error_return; diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index b55da39fde5..6a0b1783088 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -171,6 +171,77 @@ CREATE PUBLICATION testpub_parsertst FOR TABLES IN SCHEMA foo, test.foo; ERROR: invalid schema name LINE 1: ...CATION testpub_parsertst FOR TABLES IN SCHEMA foo, test.foo; ^ +-- create schema names with embedded quotes or backslashes +-- should be able to describe them in psql +CREATE SCHEMA "test-schema-embedded ' quote"; +CREATE SCHEMA "test-schema-embedded \ backslash"; +\dn "test-schema-embedded ' quote" + List of schemas + Name | Owner +------------------------------+-------------------------- + test-schema-embedded ' quote | regress_publication_user +(1 row) + +\dn "test-schema-embedded \ backslash" + List of schemas + Name | Owner +----------------------------------+-------------------------- + test-schema-embedded \ backslash | regress_publication_user +(1 row) + +\dn+ test-schema-embedded* + List of schemas + Name | Owner | Access privileges | Description +----------------------------------+--------------------------+-------------------+------------- + test-schema-embedded ' quote | regress_publication_user | | + test-schema-embedded \ backslash | regress_publication_user | | +(2 rows) + +-- should be able to publish them, and see that they are published +CREATE TABLE "test-schema-embedded ' quote"."test-table-embedded ' quote"("test-col1-embedded ' quote" int, "test-col2-embedded ' quote" int); +CREATE TABLE "test-schema-embedded \ backslash".tab1(a int); +CREATE PUBLICATION regress_pub_embedded1 FOR TABLES IN SCHEMA "test-schema-embedded ' quote"; +CREATE PUBLICATION regress_pub_embedded2 FOR ALL TABLES EXCEPT (TABLE "test-schema-embedded ' quote"."test-table-embedded ' quote"); +\dn "test-schema-embedded ' quote" + List of schemas + Name | Owner +------------------------------+-------------------------- + test-schema-embedded ' quote | regress_publication_user +Included in publications: + "regress_pub_embedded1" + +\d "test-schema-embedded ' quote"."test-table-embedded ' quote" + Table "test-schema-embedded ' quote.test-table-embedded ' quote" + Column | Type | Collation | Nullable | Default +----------------------------+---------+-----------+----------+--------- + test-col1-embedded ' quote | integer | | | + test-col2-embedded ' quote | integer | | | +Included in publications: + "regress_pub_embedded1" + "testpub_foralltables" +Excluded from publications: + "regress_pub_embedded2" + +\dRp+ regress_pub_embedded* + Publication regress_pub_embedded1 + Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- + regress_publication_user | f | f | t | t | t | t | none | f | +Tables from schemas: + "test-schema-embedded ' quote" + + Publication regress_pub_embedded2 + Owner | All tables | All sequences | Inserts | Updates | Deletes | Truncates | Generated columns | Via root | Description +--------------------------+------------+---------------+---------+---------+---------+-----------+-------------------+----------+------------- + regress_publication_user | t | f | t | t | t | t | none | f | +Except tables: + "test-schema-embedded ' quote.test-table-embedded ' quote" + +DROP PUBLICATION regress_pub_embedded1, regress_pub_embedded2; +DROP SCHEMA "test-schema-embedded ' quote" CASCADE; +NOTICE: drop cascades to table "test-schema-embedded ' quote"."test-table-embedded ' quote" +DROP SCHEMA "test-schema-embedded \ backslash" CASCADE; +NOTICE: drop cascades to table "test-schema-embedded \ backslash".tab1 -- should be able to add a table of the same schema to the schema publication ALTER PUBLICATION testpub_forschema ADD TABLE pub_test.testpub_nopk; \dRp+ testpub_forschema diff --git a/src/test/regress/sql/publication.sql b/src/test/regress/sql/publication.sql index e3dbb2bc57c..5d9186c8014 100644 --- a/src/test/regress/sql/publication.sql +++ b/src/test/regress/sql/publication.sql @@ -90,6 +90,29 @@ RESET client_min_messages; CREATE PUBLICATION testpub_parsertst FOR TABLE pub_test.testpub_nopk, CURRENT_SCHEMA; CREATE PUBLICATION testpub_parsertst FOR TABLES IN SCHEMA foo, test.foo; +-- create schema names with embedded quotes or backslashes +-- should be able to describe them in psql +CREATE SCHEMA "test-schema-embedded ' quote"; +CREATE SCHEMA "test-schema-embedded \ backslash"; +\dn "test-schema-embedded ' quote" +\dn "test-schema-embedded \ backslash" +\dn+ test-schema-embedded* + +-- should be able to publish them, and see that they are published +CREATE TABLE "test-schema-embedded ' quote"."test-table-embedded ' quote"("test-col1-embedded ' quote" int, "test-col2-embedded ' quote" int); +CREATE TABLE "test-schema-embedded \ backslash".tab1(a int); + +CREATE PUBLICATION regress_pub_embedded1 FOR TABLES IN SCHEMA "test-schema-embedded ' quote"; +CREATE PUBLICATION regress_pub_embedded2 FOR ALL TABLES EXCEPT (TABLE "test-schema-embedded ' quote"."test-table-embedded ' quote"); + +\dn "test-schema-embedded ' quote" +\d "test-schema-embedded ' quote"."test-table-embedded ' quote" +\dRp+ regress_pub_embedded* + +DROP PUBLICATION regress_pub_embedded1, regress_pub_embedded2; +DROP SCHEMA "test-schema-embedded ' quote" CASCADE; +DROP SCHEMA "test-schema-embedded \ backslash" CASCADE; + -- should be able to add a table of the same schema to the schema publication ALTER PUBLICATION testpub_forschema ADD TABLE pub_test.testpub_nopk; \dRp+ testpub_forschema -- 2.47.3