| From: | Peter Smith <smithpb2250(at)gmail(dot)com> |
|---|---|
| To: | "Jonathan Gonzalez V(dot)" <jonathan(dot)abdiel(at)gmail(dot)com> |
| Cc: | pgsql-hackers(at)lists(dot)postgresql(dot)org |
| Subject: | Re: Add pg_get_publication_ddl function |
| Date: | 2026-05-20 09:39:14 |
| Message-ID: | CAHut+Pvyd3vN_LV8ppyX6Vu7pKdBhC5M3_zHN7gdJCvz1=kKHQ@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
Hi.
Some review comments for the v2-0001 patch.
======
doc/src/sgml/func/func-info.sgml
(9.28.13. Get Object DDL Functions)
1.
+ <para role="func_signature">
+ <function>pg_get_publication_ddl</function>
+ ( <parameter>publication</parameter> <type>text</type>
+ <optional>, <literal>VARIADIC</literal> <parameter>options</parameter>
+ <type>text</type> </optional> )
+ <returnvalue>setof text</returnvalue>
+ </para>
I think "pubname" might be a more meaningful name for the first parameter.
~~~
2.
+ <para>
+ Reconstructs the <command>CREATE PUBLICATION</command> statement for
+ the specified publication (by OID or name), followed by an
+ <command>ALTER PUBLICATION ... OWNER TO</command> statement (the
+ <command>CREATE PUBLICATION</command> grammar has no
+ <literal>OWNER</literal> clause). Each statement is returned as a
+ separate row. An error is raised if no publication with the supplied
+ OID or name exists. When the publication was created with
+ <literal>FOR ALL TABLES, ALL SEQUENCES</literal>, the emitted
+ statement always lists <literal>ALL TABLES</literal> before
+ <literal>ALL SEQUENCES</literal> regardless of the original order.
+ The following options are supported:
+ <literal>pretty</literal> (boolean) for formatted output and
+ <literal>owner</literal> (boolean) to include
+ <literal>OWNER</literal>.
+ </para></entry>
2a.
That "CREATE PUBLICATION" should <link> back to the CREATE PUBLICATION
docs page.
~
2b.
It is overkill to mention about the potential reordering of ALL TABLES
and ALL SEQUENCES.
Apart from being unnecessary, there are many other things can also be
rearranged which are not mentioned:
- TABLES and ALL TABLES IN SCHEMA clauses might be different order
than specified
- The publication parameters might be in a different order than specified
- The values of 'publish' parameter might be different order than specified
- etc.
~~~
GENERAL
3.
It would be better if the the rows of "Table 9.96" were in alphabetical order.
======
src/backend/utils/adt/ddlutils.c
pg_get_publication_ddl_internal:
4.
+ if (pub->allsequences)
+ appendStringInfo(buf,
+ "%sALL SEQUENCES",
+ pub->alltables ? ", " : "");
Maybe better to avoid tricky format strings.
SUGGESTION
if (pub->allsequences)
{
if (pub->alltables)
appendStringInfo(buf, ", ");
appendStringInfo("ALL SEQUENCES");
}
~~~
5.
+ if (pub_incl_relids != NIL)
+ {
+ ListCell *pub_cell;
+ char *schemaname = NULL;
+ char *tablename;
+
+ append_ddl_option(buf, pretty, 4, "FOR TABLE ");
+
+ /*
+ * Publication can have table relations
+ */
+ foreach(pub_cell, pub_incl_relids)
Maybe that comment belongs earlier (above the if).
~~~
6.
+ appendStringInfo(buf, "%s%s",
+ foreach_current_index(pub_cell) > 0 ? ", " : "",
+ quote_qualified_identifier(schemaname, tablename));
Another place where avoiding a tricky format string may be tidier.
SUGGESTION
if (foreach_current_index(pub_cell) > 0)
appendStringInfo(buf, ", ");
appendStringInfo(buf, "%s", quote_qualified_identifier(schemaname, tablename));
~~~
7.
+ pubtuple = SearchSysCache2(PUBLICATIONRELMAP, ObjectIdGetDatum(relid),
+ ObjectIdGetDatum(pub->oid));
+
+ if (!HeapTupleIsValid(pubtuple))
+ elog(ERROR,
+ "cache lookup failed for publication relation %u in publication %u",
+ relid, pub->oid);
7a.
Maybe blank line here is not wanted.
~
7b.
Don't need to say "publication" 2x.
/publication relation/relation/
~~~
8.
+ /* If non-null, we have a list of columns to publish */
+ if (!cols_nulls)
SUGGESTION FOR THE COMMENT
Does this table specify a column-list?
~~~
9.
+ appendStringInfo(buf, "%s%s",
+ bms_member_index(attmap, attnum) ? ", " : "",
+ quote_identifier(get_attname(relid, attnum, true)));
Another place where avoiding a tricky format string may be tidier.
(change similar to previous review comments)
~~~
10.
+ /*
+ * If there is a condition it goes after the columns. We can have
+ * conditions without columns as well.
+ */
+ if (!condition_nulls)
10a.
The earlier assignment to 'conditions' should be moved to be directly
above here.
~
10b.
BTW, it is called a "row filter" so maybe it is better to refer to
that in the comments/vars instead of the generic sounding "condition".
~~~
11.
+ /* If we have schemas, they will go right before the WITH */
The kind of comments that just say "this-goes-after-that" or
"this-goes-after-that" are not very useful, because it is obvious from
the code logic that some appendStringInfo comes before or after
another one.
~~~
12.
+ /*
+ * Schemas can be preceded by a list of tables. When they are, the
+ * "TABLES IN SCHEMA" stays inline as a continuation of the existing
+ * FOR clause; otherwise it starts the FOR clause on its own line in
+ * pretty mode.
+ */
IMO it would be better for the FOR TABLE IN SCHEMA to come *before*
the specific tables in FOR TABLE.
e.g. For the case when there are specified tables "absorbed" into the
same named schemas I think it is more natural to see the schemas
first.
CREATE PUBLICATION mypub FOR TABLES IN SCHEMA s, TABLE s.t1;
~~~
13.
+ appendStringInfo(buf, "%s %s",
+ foreach_current_index(schema_cell) > 0 ? "," : "",
+ quote_identifier(nspname));
Another place where avoiding a tricky format string may be tidier.
(change similar to previous review comments)
~~~
14.
+ if (pub_excl_relids != NIL)
+ {
+ ListCell *excl_cell;
+ char *schemaname = NULL;
+
+ appendStringInfoString(buf, " EXCEPT (TABLE ");
The EXCEPT clause is currently permitted only with FOR ALL TABLES, so
it would be better moving this to earlier in this function where
pub->alltables was handled.
~~~
15.
+ appendStringInfo(buf, "%s%s",
+ foreach_current_index(excl_cell) > 0 ? ", " : "",
+ quote_qualified_identifier(schemaname, NameStr(reltup->relname)));
Another place where avoiding a tricky format string may be tidier.
(change similar to previous review comments)
~~~
16.
+ /*
+ * We need to know if we're the second permission added to prefix with a
+ * ", " string
+ */
+ if (pub->pubactions.pubinsert)
+ {
+ /*
+ * By precedence we know that the insert will always be first, no need
+ * to check previous values
+ */
+ appendStringInfoString(buf, "insert");
Both these comments are doing little more than just saying the same as
the code. IMO they are not needed.
~~~
17.
+ if (pub->pubactions.pubinsert)
+ {
+ /*
+ * By precedence we know that the insert will always be first, no need
+ * to check previous values
+ */
+ appendStringInfoString(buf, "insert");
+ first_perm = false;
+ }
+
+ if (pub->pubactions.pubupdate)
+ {
+ appendStringInfo(buf, "%supdate", first_perm ? "" : ", ");
+ first_perm = false;
+ }
+ if (pub->pubactions.pubdelete)
+ {
+ appendStringInfo(buf, "%sdelete", first_perm ? "" : ", ");
+ first_perm = false;
+ }
+
+ if (pub->pubactions.pubtruncate)
+ {
+ appendStringInfo(buf, "%struncate", first_perm ? "" : ", ");
+ }
+
17a.
There are some random blank lines that seem unnecessary.
~
17b.
IMO it is tidier to simply append the string you want, instead of
using a trick format string.
SUGGESTION (compare with patch)
if (pub->pubactions.pubinsert)
{
appendStringInfoString(buf, "insert");
first_perm = false;
}
if (pub->pubactions.pubupdate)
{
appendStringInfo(buf, first_perm ? "update" : ",update");
first_perm = false;
}
if (pub->pubactions.pubdelete)
{
appendStringInfo(buf, first_perm ? "delete" : ",delete");
first_perm = false;
}
if (pub->pubactions.pubtruncate)
{
appendStringInfo(buf, first_perm ? "truncate" : ",truncate");
}
======
src/test/regress/expected/publication_ddl.out
18.
+ CREATE PUBLICATION testpub_ddl_1
+
+ WITH (publish='insert, update, delete, truncate',
publish_generated_columns='none', publish_via_partition_root='false');
~
This "pretty" output appears to have "+" garbage in it. What's that
about -- it looks like some sort of line continuation character? Can
it be removed?
~~~
19.
The "pretty" output might be improved if each of the publication
options was on a new line.
~~~
20.
The generated boolean values (e.g. 'true'/'false') do not need to be quoted.
======
src/test/regress/sql/publication_ddl.sql
(Here are lots of test review comments; the first group are are
general so might apply to multiple test cases).
21.
I think you can create all the necessary schema and tables together
up-front instead of scatering them through the file.
~~~
22.
Make use of the proper publication teminology like "Column Lists" and
"Row Filters" instead of vague
"columns" and "conditions".
~~~
23.
Here is an idea:
Instead of having dozens of test publications, just have 1 test
publication, which you CREATE/DROP for each test case.
Then, since there is a fixed name publication (e.g. "mypub") for
everything, you can make a subroutine to encapsulate the common code:
+SELECT pg_get_publication_ddl('mypub');
+SELECT pg_get_publication_ddl((SELECT oid FROM pg_publication WHERE
pubname='mypub'));
+SELECT pg_get_publication_ddl('mypub', 'pretty', 'true');
It means your test .sql file can become much shorter/simpler I think.
~~~
24.
There is duplication of some tests:
e.g.
+-- columns in publication must be quoted
and
+-- identifiers that require quoting: publication, schema, table and column
~~~
25.
It is not needed to quote the booleans 'true'/'false' for the options.
//////
26.
+-- create base table to test basic table publication
What does "basic table publication" mean? I expect it means different
things to different people. Better to be explicit about what this is
really testing.
~~~
27.
+-- create publication for one table with two columns and a condition
with an expression
What does "with an expression" mean? All Row-Filters are expressions
aren't they?
~~~
28.
+-- create a publication for a list of tables
Not really describing what this test is doing, which is mixing FOR
TABLE and FOR TABLES IN SCHEMA.
~~~
29.
+CREATE PUBLICATION testpub_ddl_part3 FOR TABLE testpub_ddl_part WITH
(publish_via_partition_root='true');
I guess it make no difference since these are only DDL syntax tests,
but it didn't really make much sense to set
publish_via_partition_root=true when testpub_ddl_part is the ROOT
anyway.
~~~
30.
+-- create publication for all tables except two tables
Actually this is also combining with an ALL SEQUENCES test.
~~~
31.
+-- cleanup publications
+DROP PUBLICATION testpub_ddl_1;
+DROP PUBLICATION testpub_ddl_2;
+DROP PUBLICATION testpub_ddl_3;
+DROP PUBLICATION testpub_ddl_4;
+DROP PUBLICATION testpub_ddl_5;
+DROP PUBLICATION testpub_ddl_6;
+DROP PUBLICATION testpub_ddl_7;
+DROP PUBLICATION testpub_ddl_8;
+DROP PUBLICATION testpub_ddl_9;
+DROP PUBLICATION testpub_ddl_10;
+DROP PUBLICATION testpub_ddl_schema_1;
+DROP PUBLICATION testpub_ddl_schema_2;
+DROP PUBLICATION testpub_ddl_schema_3;
+DROP PUBLICATION testpub_ddl_schema_4;
+DROP PUBLICATION testpub_ddl_schema_5;
+DROP PUBLICATION testpub_ddl_part1;
+DROP PUBLICATION testpub_ddl_part2;
+DROP PUBLICATION testpub_ddl_part3;
+DROP PUBLICATION testpub_ddl_part4;
+DROP PUBLICATION "Quoted Pub";
+DROP PUBLICATION testpub_ddl_except1;
+DROP PUBLICATION testpub_ddl_except2;
As per earlier review comment IMO it would make the test code simpler
to have just 1 publication that you CREATE/DROP om the fly.
~~~
32.
+-- cleanup tables in schemas
Not sure why this is done separately. Probably easier just to drop the
schemas with CASCADE so their tables will be auto-deleted.
======
Kind Regards,
Peter Smith.
Fujitsu Australia
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Maksim.Melnikov | 2026-05-20 09:51:59 | Re: Dump statistic issue with index on expressions |
| Previous Message | vignesh C | 2026-05-20 09:35:13 | Re: Proposal: Conflict log history table for Logical Replication |