Re: Support logical replication of DDLs

From: Peter Smith <smithpb2250(at)gmail(dot)com>
To: Zheng Li <zhengli10(at)gmail(dot)com>
Cc: Ajin Cherian <itsajin(at)gmail(dot)com>, vignesh C <vignesh21(at)gmail(dot)com>, Dilip Kumar <dilipbalaut(at)gmail(dot)com>, Alvaro Herrera <alvherre(at)alvh(dot)no-ip(dot)org>, "houzj(dot)fnst(at)fujitsu(dot)com" <houzj(dot)fnst(at)fujitsu(dot)com>, Amit Kapila <amit(dot)kapila16(at)gmail(dot)com>, Masahiko Sawada <sawada(dot)mshk(at)gmail(dot)com>, Japin Li <japinli(at)hotmail(dot)com>, rajesh singarapu <rajesh(dot)rs0541(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Support logical replication of DDLs
Date: 2022-11-11 05:17:58
Message-ID: CAHut+PsFwO7xOpng59CXVk-6_1vSj-TTYuy0Lwunj-sdxMBvbg@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-general pgsql-hackers

On Fri, Nov 11, 2022 at 4:09 PM Peter Smith <smithpb2250(at)gmail(dot)com> wrote:
>
> On Fri, Nov 11, 2022 at 3:47 PM Peter Smith <smithpb2250(at)gmail(dot)com> wrote:
> >
> > Here are more review comments for the v32-0001 file ddl_deparse.c
> >
> > *** NOTE - my review post became too big, so I split it into smaller parts.
>

THIS IS PART 3 OF 4.

=======

src/backend/commands/ddl_deparse.c

50. deparse_DefineStmt_Operator

+/*
+ * Deparse a DefineStmt (CREATE OPERATOR)
+ *
+ * Given a trigger OID and the parse tree that created it, return an ObjTree
+ * representing the creation command.
+ */
+static ObjTree *
+deparse_DefineStmt_Operator(Oid objectId, DefineStmt *define)

"trigger OID" ?? Is that right?

~

51.

/* MERGES */
tmpobj = new_objtree_VA("MERGES", 1,
"clause", ObjTypeString, "merges");
if (!oprForm->oprcanmerge)
append_bool_object(tmpobj, "present", false);
list = lappend(list, new_object_object(tmpobj));

/* HASHES */
tmpobj = new_objtree_VA("HASHES", 1,
"clause", ObjTypeString, "hashes");
if (!oprForm->oprcanhash)
append_bool_object(tmpobj, "present", false);
list = lappend(list, new_object_object(tmpobj));

Maybe HASHES and MERGES should be done in a different order, just to
be consistent with the PG documentation [2].

------

52. deparse_DefineStmt_Type

+ /* Shortcut processing for shell types. */
+ if (!typForm->typisdefined)
+ {
+ stmt = new_objtree_VA("CREATE TYPE", 0);
+ append_object_object(stmt, "%{identity}D",
+ new_objtree_for_qualname(typForm->typnamespace,
+ NameStr(typForm->typname)));
+ append_bool_object(stmt, "present", true);
+ ReleaseSysCache(typTup);
+ return stmt;
+ }
+
+ stmt = new_objtree_VA("CREATE TYPE", 0);
+ append_object_object(stmt, "%{identity}D",
+ new_objtree_for_qualname(typForm->typnamespace,
+ NameStr(typForm->typname)));
+ append_bool_object(stmt, "present", true);

52a.
This code looked strange because everything is the same except the
Release/return, so IMO it should be refactored to use the common code.

~

52b.
The VA(0 args) should be combined with the subsequent appends to use
fewer append_XXX calls.

~

53.
Is it necessary to say append_bool_object(stmt, "present", true); ? --
I'd assumed that is the default unless it explicitly says false.

~

54.

/* INPUT */
tmp = new_objtree_VA("(INPUT=", 1,
"clause", ObjTypeString, "input");
append_object_object(tmp, "%{procedure}D",
new_objtree_for_qualname_id(ProcedureRelationId,
typForm->typinput));
list = lappend(list, new_object_object(tmp));

/* OUTPUT */
tmp = new_objtree_VA("OUTPUT=", 1,
"clause", ObjTypeString, "output");
append_object_object(tmp, "%{procedure}D",
new_objtree_for_qualname_id(ProcedureRelationId,
typForm->typoutput));
list = lappend(list, new_object_object(tmp));

These could each be simplified into single VA() function calls, the
same as was done in deparse_DefineStmt_Operator PROCEDURE.

And the same comment applies to other parts. e.g.:
- /* CATEGORY */
- /* ALIGNMENT */
- STORAGE

~

55.

+ tmp = new_objtree_VA("STORAGE=", 1,
+ "clause", ObjTypeString, "storage");

Missing comment above this to say /* STORAGE */

~

56.

+ /* INTERNALLENGTH */
+ if (typForm->typlen == -1)
+ {
+ tmp = new_objtree_VA("INTERNALLENGTH=VARIABLE", 0);
+ }
+ else
+ {
+ tmp = new_objtree_VA("INTERNALLENGTH=%{typlen}n", 1,
+ "typlen", ObjTypeInteger, typForm->typlen);
+ }

56a.
The VA(args = 0) does not need to be a VA function.

~

56b.
The { } blocks are unnecessary

------

57. deparse_DefineStmt_TSConfig

+
+static ObjTree *
+deparse_DefineStmt_TSConfig(Oid objectId, DefineStmt *define,
+ ObjectAddress copied)

Missing function comment.

~

58.

+ stmt = new_objtree("CREATE");
+
+ append_object_object(stmt, "TEXT SEARCH CONFIGURATION %{identity}D",
+ new_objtree_for_qualname(tscForm->cfgnamespace,
+ NameStr(tscForm->cfgname)));

Why not combine these using VA() function?

~

59.

+ list = NIL;
+ /* COPY */

Just assign NIL when declared.

~

60.

+ if (copied.objectId != InvalidOid)

Use OidIsValid macro.

------

61. deparse_DefineStmt_TSParser

+
+static ObjTree *
+deparse_DefineStmt_TSParser(Oid objectId, DefineStmt *define)

Missing function comment.

~

62.

+ stmt = new_objtree("CREATE");
+
+ append_object_object(stmt, "TEXT SEARCH PARSER %{identity}D",
+ new_objtree_for_qualname(tspForm->prsnamespace,
+ NameStr(tspForm->prsname)));

Why not combine as a single VA() function call?

~

63.

+ list = NIL;

Just assign NIL when declared

~

64.

tmp = new_objtree_VA("START=", 1,
"clause", ObjTypeString, "start");
append_object_object(tmp, "%{procedure}D",
new_objtree_for_qualname_id(ProcedureRelationId,
tspForm->prsstart));

Easily combined to be a single VA() function call.

The same comment applies for
- /* GETTOKEN */
- /* END */
- /* LEXTYPES */

------

65. deparse_DefineStmt_TSDictionary

+static ObjTree *
+deparse_DefineStmt_TSDictionary(Oid objectId, DefineStmt *define)

Missing function comment.

~

66.

+ stmt = new_objtree("CREATE");
+
+ append_object_object(stmt, "TEXT SEARCH DICTIONARY %{identity}D",
+ new_objtree_for_qualname(tsdForm->dictnamespace,
+ NameStr(tsdForm->dictname)));

Why not combine this as a single VA() function call?

~

67.

+ list = NIL;

Just assign NIL when declared

~

68.

+ tmp = new_objtree_VA("", 0);

Don't need VA() function for 0 args.

------

69. deparse_DefineStmt_TSTemplate

+static ObjTree *
+deparse_DefineStmt_TSTemplate(Oid objectId, DefineStmt *define)

Missing function comment.

~

70.

+ stmt = new_objtree("CREATE");
+
+ append_object_object(stmt, "TEXT SEARCH TEMPLATE %{identity}D",
+ new_objtree_for_qualname(tstForm->tmplnamespace,
+ NameStr(tstForm->tmplname)));

Combine this to be a single VA() function call.

~

71.

+ list = NIL;

Just assign NIL when declared

~

72.

+ tmp = new_objtree_VA("LEXIZE=", 1,
+ "clause", ObjTypeString, "lexize");
+ append_object_object(tmp, "%{procedure}D",
+ new_objtree_for_qualname_id(ProcedureRelationId,
+ tstForm->tmpllexize));

Combine this to be a single VA() function call.

------

73. deparse_AlterTSConfigurationStmt

+static ObjTree *
+deparse_AlterTSConfigurationStmt(CollectedCommand *cmd)

Missing function comment.

~

74.

+ /* determine the format string appropriate to each subcommand */
+ switch (node->kind)

Uppercase comment

~

75.

+ tmp = new_objtree_VA("IF EXISTS", 0);

Should not use a VA() function with 0 args.

~

76.

+ case ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN:
+ append_object_object(config, "%{identity}D ALTER MAPPING",
+ new_objtree_for_qualname_id(cmd->d.atscfg.address.classId,
+ cmd->d.atscfg.address.objectId));
+ break;
+
+ case ALTER_TSCONFIG_REPLACE_DICT:
+ append_object_object(config, "%{identity}D ALTER MAPPING",
+ new_objtree_for_qualname_id(cmd->d.atscfg.address.classId,
+ cmd->d.atscfg.address.objectId));
+ break;
+
+ case ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN:
+ append_object_object(config, "%{identity}D ALTER MAPPING",
+ new_objtree_for_qualname_id(cmd->d.atscfg.address.classId,
+ cmd->d.atscfg.address.objectId));
+ break;

If all these 3 cases have identical code then why repeat it three times?

~

77.

+ /* add further subcommand-specific elements */

Uppercase comment

~

78.

+ /* the REPLACE forms want old and new dictionaries */
+ Assert(cmd->d.atscfg.ndicts == 2);

Uppercase comment.

------

79. deparse_AlterTSDictionaryStmt

+
+static ObjTree *
+deparse_AlterTSDictionaryStmt(Oid objectId, Node *parsetree)

Missing function comment

~

80.

+ alterTSD = new_objtree("ALTER TEXT SEARCH DICTIONARY");
+
+ append_object_object(alterTSD, "%{identity}D",
+ new_objtree_for_qualname(tsdForm->dictnamespace,
+ NameStr(tsdForm->dictname)));

Combine this as a sing VA() function call

~

81.

+ tmp = new_objtree_VA("", 0);

Don't use the VA() function for 0 args.

------

82. deparse_RelSetOptions

+ if (is_reset)
+ fmt = "RESET ";
+ else
+ fmt = "SET ";
+
+ relset = new_objtree(fmt);

82a.
Those format trailing spaces are a bit unusual. The append_XXX will
take care of space separators anyhow so it is not needed like this.

~

82b.
This can all be simplified to one line:

relset = new_objtree(is_reset ? "RESET" : "SET");

------

83. deparse_ViewStmt

+ * Given a view OID and the parsetree that created it, return an ObjTree
+ * representing the creation command.
+ */

Be consistent with other function headers:

"parsetree" -> "parse tree".

~

84.

+ viewStmt = new_objtree("CREATE");
+
+ append_string_object(viewStmt, "%{or_replace}s",
+ node->replace ? "OR REPLACE" : "");
+
+ append_string_object(viewStmt, "%{persistence}s",
+ get_persistence_str(relation->rd_rel->relpersistence));
+
+ tmp = new_objtree_for_qualname(relation->rd_rel->relnamespace,
+ RelationGetRelationName(relation));
+
+ append_object_object(viewStmt, "VIEW %{identity}D", tmp);
+
+ append_string_object(viewStmt, "AS %{query}s",
+ pg_get_viewdef_internal(objectId));

IMO all of this can be combined in a single VA() function call.

------

85. deparse_CreateTableAsStmt_vanilla

+/*
+ * Deparse CREATE Materialized View statement, it is a variant of
CreateTableAsStmt
+ *
+ * Note that CREATE TABLE AS SELECT INTO can also be deparsed by
+ * deparse_CreateTableAsStmt to remove the SELECT INTO clause.
+ */
+static ObjTree *
+deparse_CreateTableAsStmt_vanilla(Oid objectId, Node *parsetree)

The function comment refers to 'deparse_CreateTableAsStmt' but I don't
see any such function. Maybe this was renamed causing the comment
became stale?

~

86.

+ /* Add identity */
+ append_object_object(createStmt, "%{identity}D",
+ new_objtree_for_qualname_id(RelationRelationId,
+ objectId));

This could be included as another arg of the preceding VA() call/

~

87.

+ /* COLLUMNS clause */
+ if (node->into->colNames == NIL)
+ tmp = new_objtree_VA("", 1,
+ "present", ObjTypeBool, false);
+ else

87a.
Typo "COLLUMNS"

~

87b.
It might be more usual/natural to reverse this if/else to check the
list is NOT empty. e.g.

if (node->into->colNames)
...
else
tmp = new_objtree_VA("", 1,
"present", ObjTypeBool, false);

~

88.

+ tmp = new_objtree("USING");
+ if (node->into->accessMethod)
+ append_string_object(tmp, "%{access_method}I", node->into->accessMethod);
+ else
+ {
+ append_null_object(tmp, "%{access_method}I");
+ append_bool_object(tmp, "present", false);
+ }

I'm not sure why a null object is necessary when present = false.

~

89.

+ /* WITH clause */
+ tmp = new_objtree_VA("WITH", 0);

VA() function call is not needed when there are 0 args.

~

90.

+ /* TABLESPACE clause */
+ tmp = new_objtree_VA("TABLESPACE %{tablespace}I", 0);

VA() function call nor needed when there are 0 args.

~

91.

+ else
+ {
+ append_null_object(tmp, "%{tablespace}I");
+ append_bool_object(tmp, "present", false);
+ }

I'm not sure why a null object is necessary when present = false.

~

92.

+ /* add a WITH NO DATA clause */
+ tmp = new_objtree_VA("WITH NO DATA", 1,
+ "present", ObjTypeBool,
+ node->into->skipData ? true : false);
+ append_object_object(createStmt, "%{with_no_data}s", tmp);

92a.
Uppercase comment.

~

92b.
It is a bit confusing that this style of specifying empty tree (just
saying present/not present) is used here. But elsewhere in this patch
for similar syntax it just adds text or an empty string.
e.g.
+ append_string_object(renameStmt, "%{if_exists}s",
+ node->missing_ok ? "IF EXISTS" : "");

IMO it's better to apply a consistent deparse approach for everything.
But without documentation of the deparse structure, it is kind of
impossible to know even what *are* the rules?

------

93. deparse_CreateTrigStmt

+ trigger = new_objtree("CREATE");
+
+ append_string_object(trigger, "%{constraint}s",
+ node->isconstraint ? "CONSTRAINT" : "");
+
+ append_string_object(trigger, "TRIGGER %{name}I", node->trigname);

All this can be combined into a single VA() call.

~

94.

+ if (node->timing == TRIGGER_TYPE_BEFORE)
+ append_string_object(trigger, "%{time}s", "BEFORE");
+ else if (node->timing == TRIGGER_TYPE_AFTER)
+ append_string_object(trigger, "%{time}s", "AFTER");
+ else if (node->timing == TRIGGER_TYPE_INSTEAD)
+ append_string_object(trigger, "%{time}s", "INSTEAD OF");
+ else
+ elog(ERROR, "unrecognized trigger timing type %d", node->timing);

It might be better to assign the value to a char* and then just have
only a single append_string_object() call.

char *tval =
node->timing == TRIGGER_TYPE_BEFORE ? "BEFORE" :
node->timing == TRIGGER_TYPE_AFTER ? "AFTER" :
node->timing == TRIGGER_TYPE_INSTEAD ? "INSTEAD OF" :
NULL;
if (tval == NULL)
elog(ERROR, "unrecognized trigger timing type %d", node->timing);
append_string_object(trigger, "%{time}s", tval);

~

95.

+ tmpobj = new_objtree_VA("FROM", 0);

VA() function call is not needed for 0 args.

~

96.

+ tmpobj = new_objtree_VA("WHEN", 0);

VA() function call is not needed for 0 args.

~

97.

Should use consistent wording for unexpected nulls.

e.g.1
+ if (isnull)
+ elog(ERROR, "bogus NULL tgqual");

e.g.2
+ if (isnull)
+ elog(ERROR, "invalid NULL tgargs");

~

98.

+ append_format_string(tmpobj, "(");
+ append_array_object(tmpobj, "%{args:, }L", list); /* might be NIL */
+ append_format_string(tmpobj, ")");

IMO probably that can be a single call to append_array_object which
includes the enclosing parens.

------
[2] CREATE OPERATOR -
https://www.postgresql.org/docs/current/sql-createoperator.html

Kind Regards,
Peter Smith.
Fujitsu Australia

In response to

Responses

Browse pgsql-general by date

  From Date Subject
Next Message Peter Smith 2022-11-11 05:33:04 Re: Support logical replication of DDLs
Previous Message Peter Smith 2022-11-11 05:09:04 Re: Support logical replication of DDLs

Browse pgsql-hackers by date

  From Date Subject
Next Message Peter Smith 2022-11-11 05:33:04 Re: Support logical replication of DDLs
Previous Message Michael Paquier 2022-11-11 05:11:08 Re: Unit tests for SLRU