On Wed, Jun 18, 2025 at 8:13 AM Fujii Masao <[email protected]> wrote:
>
> I'm aware of a related open item [1] affecting both v17 and v18,
> but this seems like a separate issue, since it relates to a new v18 feature...
> Or we should treat them the same?
>
I think they are separate issues.
for check constraint that dump willed separately:
check constraint comments will go through dumpConstraint,
dumpTableConstraintComment
for check constraint that dump won't dump separately:
check constraint comments will go through dumpTableSchema,
dumpTableConstraintComment
Similarly we don't need to worry about not-null constraints that are
dumped separately.
dumpConstraint, dumpTableConstraintComment will do the job.
however per comment from collectComments say
"/* We needn't remember comments that don't match an */"
there is no ConstraintInfo for these inlined not-null, that means
``static CommentItem *comments = NULL;``
does not hold any comments for these inlined-null constraints.
so for
CREATE TABLE t (i int);
ALTER TABLE t ADD CONSTRAINT my_not_null NOT NULL i;
COMMENT ON CONSTRAINT my_not_null ON t IS 'my not null';
we can not locate the not-null constraint and use dumpComment to dump
the comments.
dumpComment->findComments will return nothing.
but we need to do the similar thing of dumpCommentExtended
``if (ncomments > 0){}`` code branch.
dumpTableSchema handles dumping of table column definitions, and tells us which
column print_notnull is true. Since we already know which columns have had
their not-null constraints printed, it makes sense to dump inline not-null
comments here too.
Please check the attached POC patch.
From c586c07a2c2b473f88453c12de0a08190db42a2f Mon Sep 17 00:00:00 2001
From: jian he <[email protected]>
Date: Wed, 18 Jun 2025 22:35:14 +0800
Subject: [PATCH v1 1/1] fix pg_dump not dump comments on not-null constraints
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
dumpComment doesn't handle these inlined NOT NULL constraint comments, because
collectComments doesn't collect them—they aren't associated with a separate
dumpable object. Rather than modifying collectComments, we manually dump these
inlined not-null constraint comments within dumpTableSchema.
reported by: Fujii Masao <masao(dot)fujii(at)oss(dot)nttdata(dot)com>
discussion: https://postgr.es/m/[email protected]
---
src/bin/pg_dump/pg_dump.c | 111 ++++++++++++++++++++++++++++++++++++++
1 file changed, 111 insertions(+)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7bc0724cd30..909ce30d553 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -16769,6 +16769,9 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
char *storage;
int j,
k;
+ bool *notnullprinted;
+ bool notnull_printed = false; /* at least one not-null printed */
+ notnullprinted = (bool *) pg_malloc0(tbinfo->numatts * sizeof(bool));
/* We had better have loaded per-column details about this table */
Assert(tbinfo->interesting);
@@ -17016,6 +17019,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
if (print_notnull)
{
+ notnullprinted[j] = true;
if (tbinfo->notnull_constrs[j][0] == '\0')
appendPQExpBufferStr(q, " NOT NULL");
else
@@ -17684,6 +17688,113 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
if (tbinfo->dobj.dump & DUMP_COMPONENT_SECLABEL)
dumpTableSecLabel(fout, tbinfo, reltypename);
+ /*
+ * Dump Table not-null constraint comments.
+ * Invalid not-null constraints are dumped separately. So we only need to
+ * handle comments for these "inlined" not-null constraints.
+ * collectComments function does not collect comments for inlined not-null
+ * constraints, as they don't have separate dumpable object associated with
+ * it. Therefore, we need to explicitly dump comments for these "inlined"
+ * not-null constraints now, since they won't be dumped separately.
+ */
+ for (j = 0; j < tbinfo->numatts; j++)
+ {
+ if (notnullprinted[j])
+ {
+ notnull_printed = true;
+ break;
+ }
+ }
+
+ if (!fout->dopt->no_comments &&
+ dopt->dumpSchema &&
+ fout->remoteVersion >= 180000 &&
+ notnull_printed)
+ {
+ PGresult *res;
+ int i_description;
+ int i_constrname;
+ bool firstitem = true;
+ char *qtabname;
+ const char *descr;
+ const char *constrname;
+ const char *namespace = tbinfo->dobj.namespace->dobj.name;
+ const char *owner = tbinfo->rolname;
+ PQExpBuffer query = createPQExpBuffer();
+ PQExpBuffer comments = createPQExpBuffer();
+ PQExpBuffer conprefix = createPQExpBuffer();
+ PQExpBuffer tag = createPQExpBuffer();
+ DumpId dumpId = tbinfo->dobj.dumpId;
+
+ qtabname = pg_strdup(fmtId(tbinfo->dobj.name));
+ appendPQExpBufferStr(query, "SELECT pt.description, pc.conname\n"
+ "FROM pg_catalog.pg_constraint pc\n"
+ "JOIN pg_catalog.pg_description pt\n"
+ "ON pt.classoid = pc.tableoid AND pc.oid = pt.objoid\n"
+ "WHERE contype = 'n' AND conrelid = ");
+ appendStringLiteralAH(query, fmtQualifiedDumpable(tbinfo), fout);
+
+ appendPQExpBufferStr(query, "::pg_catalog.regclass AND conkey IN (");
+ for (j = 0; j < tbinfo->numatts; j++)
+ {
+ if (notnullprinted[j])
+ {
+ Assert(tbinfo->notnull_constrs[j] != NULL);
+
+ if (firstitem)
+ firstitem = false;
+ else
+ appendPQExpBufferStr(query, ", ");
+
+ appendPQExpBuffer(query, "'{%d}'", j + 1);
+ }
+ }
+ appendPQExpBufferChar(query, ')');
+
+ res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
+ i_description = PQfnumber(res, "description");
+ i_constrname = PQfnumber(res, "conname");
+
+ for (int i = 0; i < PQntuples(res); i++)
+ {
+ descr = PQgetvalue(res, i, i_description);
+ constrname = PQgetvalue(res, i, i_constrname);
+
+ appendPQExpBuffer(conprefix, "CONSTRAINT %s ON",
+ fmtId(constrname));
+
+ appendPQExpBuffer(comments, "COMMENT ON %s ", conprefix->data);
+ if (namespace && *namespace)
+ appendPQExpBuffer(comments, "%s.", fmtId(namespace));
+ appendPQExpBuffer(comments, "%s IS ", qtabname);
+ appendStringLiteralAH(comments, descr, fout);
+ appendPQExpBufferStr(comments, ";\n");
+
+ appendPQExpBuffer(tag, "%s %s", conprefix->data, qtabname);
+
+ /* see dumpCommentExtended comments also */
+ ArchiveEntry(fout, nilCatalogId, createDumpId(),
+ ARCHIVE_OPTS(.tag = tag->data,
+ .namespace = namespace,
+ .owner = owner,
+ .description = "COMMENT",
+ .section = SECTION_NONE,
+ .createStmt = comments->data,
+ .deps = &dumpId,
+ .nDeps = 1));
+
+ resetPQExpBuffer(comments);
+ resetPQExpBuffer(tag);
+ resetPQExpBuffer(conprefix);
+ }
+ PQclear(res);
+ destroyPQExpBuffer(query);
+ destroyPQExpBuffer(conprefix);
+ destroyPQExpBuffer(comments);
+ destroyPQExpBuffer(tag);
+ free(qtabname);
+ }
+
/* Dump comments on inlined table constraints */
for (j = 0; j < tbinfo->ncheck; j++)
{
--
2.34.1