pg_dump does not dump domain not-null constraint's comments
hi.
----------------------------------------------------
begin;
create schema test;
set search_path to test;
create domain d1 as int;
alter domain d1 add constraint nn not null;
alter domain d1 add constraint cc check (value is not null);
comment on constraint nn on domain d1 is 'not null constraint on domain d1';
comment on constraint cc on domain d1 is 'check constraint on domain d1';
commit;
------the above is the test script------------------
in PG17 and master, pg_dump (--schema=test --no-owner) will only produce
COMMENT ON CONSTRAINT cc ON DOMAIN test.d1 IS 'check constraint on domain d1';
but didn't produce
COMMENT ON CONSTRAINT nn ON DOMAIN test.d1 IS 'not null constraint on
domain d1';
we should make pg_dump to produce it too?
The attached patch tries to make it produce comments on not-null
constraints on domains.
I aslo renamed struct TypeInfo fields, nDomChecks will be renamed to
nDomConstrs;
domChecks will be renamed to domConstrs.
TypeInfo->domConstrs will also include not-null constraint
information, changing from
domChecks to domConstrs makes sense, IMHO.
Attachments:
v1-0001-pg_dump-does-not-dump-domain-not-null-constraint-.patchtext/x-patch; charset=US-ASCII; name=v1-0001-pg_dump-does-not-dump-domain-not-null-constraint-.patchDownload
From fcaba2b44f62ae76404095352edcecd0cbc967ff Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Wed, 7 May 2025 16:59:35 +0800
Subject: [PATCH v1 1/1] pg_dump does not dump domain not-null constraint's
comments
this patch trying to resolve this issue.
discussion: https://postgr.es/m/
---
src/bin/pg_dump/pg_dump.c | 66 ++++++++++++++++++++++------------
src/bin/pg_dump/pg_dump.h | 4 +--
src/bin/pg_dump/pg_dump_sort.c | 5 +--
3 files changed, 46 insertions(+), 29 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e2e7975b34e..7253937f9a3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6118,8 +6118,8 @@ getTypes(Archive *fout)
/*
* If it's a domain, fetch info about its constraints, if any
*/
- tyinfo[i].nDomChecks = 0;
- tyinfo[i].domChecks = NULL;
+ tyinfo[i].nDomConstrs = 0;
+ tyinfo[i].domConstrs = NULL;
if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
tyinfo[i].typtype == TYPTYPE_DOMAIN)
getDomainConstraints(fout, &(tyinfo[i]));
@@ -8250,7 +8250,8 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
int i_tableoid,
i_oid,
i_conname,
- i_consrc;
+ i_consrc,
+ i_contype;
int ntups;
if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
@@ -8260,10 +8261,19 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
"PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
"SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
- "convalidated "
+ "convalidated, "
+ "contype "
"FROM pg_catalog.pg_constraint "
- "WHERE contypid = $1 AND contype = 'c' "
- "ORDER BY conname");
+ "WHERE contypid = $1 ");
+
+ /*
+ * in PG17 we record not-null domain constraint catalog information into
+ * pg_constraint
+ */
+ if (fout->remoteVersion < 170000)
+ appendPQExpBufferStr(query, "AND contype = 'c' ");
+
+ appendPQExpBufferStr(query, "ORDER BY conname");
ExecuteSqlStatement(fout, query->data);
@@ -8282,11 +8292,12 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
i_oid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_consrc = PQfnumber(res, "consrc");
+ i_contype = PQfnumber(res, "contype");
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
- tyinfo->nDomChecks = ntups;
- tyinfo->domChecks = constrinfo;
+ tyinfo->nDomConstrs = ntups;
+ tyinfo->domConstrs = constrinfo;
for (i = 0; i < ntups; i++)
{
@@ -8300,7 +8311,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
constrinfo[i].contable = NULL;
constrinfo[i].condomain = tyinfo;
- constrinfo[i].contype = 'c';
+ constrinfo[i].contype = *(PQgetvalue(res, i, i_contype));
constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
constrinfo[i].confrelid = InvalidOid;
constrinfo[i].conindex = 0;
@@ -12479,7 +12490,7 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
}
- if (typnotnull[0] == 't')
+ if (typnotnull[0] == 't' && fout->remoteVersion < 170000)
appendPQExpBufferStr(q, " NOT NULL");
if (typdefault != NULL)
@@ -12494,15 +12505,15 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
PQclear(res);
/*
- * Add any CHECK constraints for the domain
+ * Add any CHECK, NOT NULL constraints for the domain
*/
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ for (i = 0; i < tyinfo->nDomConstrs; i++)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ ConstraintInfo *domconstr = &(tyinfo->domConstrs[i]);
- if (!domcheck->separate)
+ if (!domconstr->separate)
appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
- fmtId(domcheck->dobj.name), domcheck->condef);
+ fmtId(domconstr->dobj.name), domconstr->condef);
}
appendPQExpBufferStr(q, ";\n");
@@ -12542,19 +12553,19 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
NULL, tyinfo->rolname, &tyinfo->dacl);
/* Dump any per-constraint comments */
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ for (i = 0; i < tyinfo->nDomConstrs; i++)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ ConstraintInfo *domconstr = &(tyinfo->domConstrs[i]);
PQExpBuffer conprefix = createPQExpBuffer();
appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
- fmtId(domcheck->dobj.name));
+ fmtId(domconstr->dobj.name));
- if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
+ if (domconstr->dobj.dump & DUMP_COMPONENT_COMMENT)
dumpComment(fout, conprefix->data, qtypname,
tyinfo->dobj.namespace->dobj.name,
tyinfo->rolname,
- domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+ domconstr->dobj.catId, 0, tyinfo->dobj.dumpId);
destroyPQExpBuffer(conprefix);
}
@@ -18370,14 +18381,23 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
.dropStmt = delq->data));
}
}
- else if (coninfo->contype == 'c' && tbinfo == NULL)
+ else if (tbinfo == NULL)
{
- /* CHECK constraint on a domain */
+ /* CHECK, NOT NULL constraint on a domain */
TypeInfo *tyinfo = coninfo->condomain;
+ Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
+
/* Ignore if not to be dumped separately */
if (coninfo->separate)
{
+ const char *keyword;
+
+ if (coninfo->contype == 'c')
+ keyword = "CHECK CONSTRAINT";
+ else
+ keyword = "CONSTRAINT";
+
appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
fmtQualifiedDumpable(tyinfo));
appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
@@ -18396,7 +18416,7 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
ARCHIVE_OPTS(.tag = tag,
.namespace = tyinfo->dobj.namespace->dobj.name,
.owner = tyinfo->rolname,
- .description = "CHECK CONSTRAINT",
+ .description = keyword,
.section = SECTION_POST_DATA,
.createStmt = q->data,
.dropStmt = delq->data));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7417eab6aef..b5e19ecc2e3 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -223,8 +223,8 @@ typedef struct _typeInfo
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
/* If it's a domain, we store links to its constraints here: */
- int nDomChecks;
- struct _constraintInfo *domChecks;
+ int nDomConstrs;
+ struct _constraintInfo *domConstrs;
} TypeInfo;
typedef struct _shellTypeInfo
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 0b0977788f1..0497d7e37fc 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -907,7 +907,7 @@ repairTableAttrDefMultiLoop(DumpableObject *tableobj,
}
/*
- * CHECK constraints on domains work just like those on tables ...
+ * CHECK, NOT NULL constraints on domains work just like those on tables ...
*/
static void
repairDomainConstraintLoop(DumpableObject *domainobj,
@@ -1177,7 +1177,6 @@ repairDependencyLoop(DumpableObject **loop,
if (nLoop == 2 &&
loop[0]->objType == DO_TYPE &&
loop[1]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[1])->contype == 'c' &&
((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
{
repairDomainConstraintLoop(loop[0], loop[1]);
@@ -1186,7 +1185,6 @@ repairDependencyLoop(DumpableObject **loop,
if (nLoop == 2 &&
loop[1]->objType == DO_TYPE &&
loop[0]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[0])->contype == 'c' &&
((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
{
repairDomainConstraintLoop(loop[1], loop[0]);
@@ -1203,7 +1201,6 @@ repairDependencyLoop(DumpableObject **loop,
for (j = 0; j < nLoop; j++)
{
if (loop[j]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[j])->contype == 'c' &&
((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
{
repairDomainConstraintMultiLoop(loop[i], loop[j]);
--
2.34.1
On 2025-May-07, jian he wrote:
in PG17 and master, pg_dump (--schema=test --no-owner)
[...]
didn't produce
COMMENT ON CONSTRAINT nn ON DOMAIN test.d1 IS 'not null constraint on
domain d1';
we should make pg_dump to produce it too?
Yes, this is clearly a pg17 bug whose fix should be backpatched.
The attached patch tries to make it produce comments on not-null
constraints on domains.
Thanks, I'll have a look.
I aslo renamed struct TypeInfo fields, nDomChecks will be renamed to
nDomConstrs; domChecks will be renamed to domConstrs.
TypeInfo->domConstrs will also include not-null constraint
information, changing from domChecks to domConstrs makes sense, IMHO.
Hmm, for a backpatch I would leave the field names alone since they are
publicly visible; we can rename separately in pg19 once it opens. Can
you resubmit splitting the renaming out to a 0002 patch?
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
"In fact, the basic problem with Perl 5's subroutines is that they're not
crufty enough, so the cruft leaks out into user-defined code instead, by
the Conservation of Cruft Principle." (Larry Wall, Apocalypse 6)
On Wed, May 7, 2025 at 5:25 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
On 2025-May-07, jian he wrote:
in PG17 and master, pg_dump (--schema=test --no-owner)
[...]
didn't produce
COMMENT ON CONSTRAINT nn ON DOMAIN test.d1 IS 'not null constraint on
domain d1';
we should make pg_dump to produce it too?Yes, this is clearly a pg17 bug whose fix should be backpatched.
The attached patch tries to make it produce comments on not-null
constraints on domains.Thanks, I'll have a look.
I aslo renamed struct TypeInfo fields, nDomChecks will be renamed to
nDomConstrs; domChecks will be renamed to domConstrs.
TypeInfo->domConstrs will also include not-null constraint
information, changing from domChecks to domConstrs makes sense, IMHO.Hmm, for a backpatch I would leave the field names alone since they are
publicly visible; we can rename separately in pg19 once it opens. Can
you resubmit splitting the renaming out to a 0002 patch?--
hi.
i didn't fully understand pg_dump perl dump test, I have added some changes on
src/bin/pg_dump/t/002_pg_dump.pl, but it fails the tests....
v2-0001 ensures pg_dump dump domain not-null constraint's comments
v2-0002 change some variable/argument/struct element name because of v2-0001.
Attachments:
v2-0001-pg_dump-does-not-dump-domain-not-null-constraint-.patchtext/x-patch; charset=US-ASCII; name=v2-0001-pg_dump-does-not-dump-domain-not-null-constraint-.patchDownload
From e1f94b2b1f21ca8f285b40efe87e1ed64e52b0d1 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Wed, 7 May 2025 22:21:52 +0800
Subject: [PATCH v2 1/2] pg_dump does not dump domain not-null constraint's
comments
If requested, we should dump comments for domain not-null constraints. Note: In
PostgreSQL 16 and earlier, these constraints do not have entries in
pg_constraint.
discussion: https://postgr.es/m/CACJufxF-0bqVR=j4jonS6N2Ka6hHUpFyu3_3TWKNhOW_4yFSSg@mail.gmail.com
---
src/bin/pg_dump/pg_dump.c | 41 ++++++++++++++++++++++++--------
src/bin/pg_dump/pg_dump_sort.c | 9 +++----
src/bin/pg_dump/t/002_pg_dump.pl | 5 ++++
3 files changed, 39 insertions(+), 16 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e2e7975b34e..ea5e2abb03f 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -8250,7 +8250,8 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
int i_tableoid,
i_oid,
i_conname,
- i_consrc;
+ i_consrc,
+ i_contype;
int ntups;
if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
@@ -8260,10 +8261,19 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
"PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
"SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
- "convalidated "
+ "convalidated, "
+ "contype "
"FROM pg_catalog.pg_constraint "
- "WHERE contypid = $1 AND contype = 'c' "
- "ORDER BY conname");
+ "WHERE contypid = $1 ");
+
+ /*
+ * in PG17 or later versions, not-null domain constraint catalog
+ * information is stored in the pg_constraint.
+ */
+ if (fout->remoteVersion < 170000)
+ appendPQExpBufferStr(query, "AND contype = 'c' ");
+
+ appendPQExpBufferStr(query, "ORDER BY conname");
ExecuteSqlStatement(fout, query->data);
@@ -8282,6 +8292,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
i_oid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_consrc = PQfnumber(res, "consrc");
+ i_contype = PQfnumber(res, "contype");
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
@@ -8300,7 +8311,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
constrinfo[i].contable = NULL;
constrinfo[i].condomain = tyinfo;
- constrinfo[i].contype = 'c';
+ constrinfo[i].contype = *(PQgetvalue(res, i, i_contype));
constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
constrinfo[i].confrelid = InvalidOid;
constrinfo[i].conindex = 0;
@@ -12479,7 +12490,8 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
}
- if (typnotnull[0] == 't')
+ /* PG17 or later versions is handled in below */
+ if (typnotnull[0] == 't' && fout->remoteVersion < 170000)
appendPQExpBufferStr(q, " NOT NULL");
if (typdefault != NULL)
@@ -12494,7 +12506,7 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
PQclear(res);
/*
- * Add any CHECK constraints for the domain
+ * Add any CHECK, NOT NULL constraints for the domain
*/
for (i = 0; i < tyinfo->nDomChecks; i++)
{
@@ -18370,14 +18382,23 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
.dropStmt = delq->data));
}
}
- else if (coninfo->contype == 'c' && tbinfo == NULL)
+ else if (tbinfo == NULL)
{
- /* CHECK constraint on a domain */
+ /* CHECK, NOT NULL constraint on a domain */
TypeInfo *tyinfo = coninfo->condomain;
+ Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
+
/* Ignore if not to be dumped separately */
if (coninfo->separate)
{
+ const char *keyword;
+
+ if (coninfo->contype == 'c')
+ keyword = "CHECK CONSTRAINT";
+ else
+ keyword = "CONSTRAINT";
+
appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
fmtQualifiedDumpable(tyinfo));
appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
@@ -18396,7 +18417,7 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
ARCHIVE_OPTS(.tag = tag,
.namespace = tyinfo->dobj.namespace->dobj.name,
.owner = tyinfo->rolname,
- .description = "CHECK CONSTRAINT",
+ .description = keyword,
.section = SECTION_POST_DATA,
.createStmt = q->data,
.dropStmt = delq->data));
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 0b0977788f1..e5506ca788c 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -907,7 +907,7 @@ repairTableAttrDefMultiLoop(DumpableObject *tableobj,
}
/*
- * CHECK constraints on domains work just like those on tables ...
+ * CHECK, NOT NULL constraints on domains work just like those on tables ...
*/
static void
repairDomainConstraintLoop(DumpableObject *domainobj,
@@ -1173,11 +1173,10 @@ repairDependencyLoop(DumpableObject **loop,
}
}
- /* Domain and CHECK constraint */
+ /* Domain and CHECK, NOT NULL constraint */
if (nLoop == 2 &&
loop[0]->objType == DO_TYPE &&
loop[1]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[1])->contype == 'c' &&
((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
{
repairDomainConstraintLoop(loop[0], loop[1]);
@@ -1186,14 +1185,13 @@ repairDependencyLoop(DumpableObject **loop,
if (nLoop == 2 &&
loop[1]->objType == DO_TYPE &&
loop[0]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[0])->contype == 'c' &&
((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
{
repairDomainConstraintLoop(loop[1], loop[0]);
return;
}
- /* Indirect loop involving domain and CHECK constraint */
+ /* Indirect loop involving domain and CHECK OR NOT NULL constraint */
if (nLoop > 2)
{
for (i = 0; i < nLoop; i++)
@@ -1203,7 +1201,6 @@ repairDependencyLoop(DumpableObject **loop,
for (j = 0; j < nLoop; j++)
{
if (loop[j]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[j])->contype == 'c' &&
((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
{
repairDomainConstraintMultiLoop(loop[i], loop[j]);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 55d892d9c16..88369bc98ae 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2289,16 +2289,21 @@ my %tests = (
create_sql => 'CREATE DOMAIN dump_test.us_postal_code AS TEXT
COLLATE "C"
DEFAULT \'10014\'
+ CONSTRAINT nn NOT NULL
CHECK(VALUE ~ \'^\d{5}$\' OR
VALUE ~ \'^\d{5}-\d{4}$\');
+ COMMENT ON CONSTRAINT nn
+ ON DOMAIN dump_test.us_postal_code IS \'not null\';
COMMENT ON CONSTRAINT us_postal_code_check
ON DOMAIN dump_test.us_postal_code IS \'check it\';',
regexp => qr/^
\QCREATE DOMAIN dump_test.us_postal_code AS text COLLATE pg_catalog."C" DEFAULT '10014'::text\E\n\s+
+ \QCONSTRAINT nn NOT NULL \E
\QCONSTRAINT us_postal_code_check CHECK \E
\Q(((VALUE ~ '^\d{5}\E
\$\Q'::text) OR (VALUE ~ '^\d{5}-\d{4}\E\$
\Q'::text)));\E(.|\n)*
+ \QCOMMENT ON CONSTRAINT nn ON DOMAIN dump_test.us_postal_code IS 'not null';\E
\QCOMMENT ON CONSTRAINT us_postal_code_check ON DOMAIN dump_test.us_postal_code IS 'check it';\E
/xm,
like =>
--
2.34.1
v2-0002-sanitize-variable-or-argument-name-in-pg_dump.patchtext/x-patch; charset=US-ASCII; name=v2-0002-sanitize-variable-or-argument-name-in-pg_dump.patchDownload
From d931c5e16b20bce05a84df7bf435763c255aa7f9 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Wed, 7 May 2025 22:28:32 +0800
Subject: [PATCH v2 2/2] sanitize variable or argument name in pg_dump
discussion: https://postgr.es/m/CACJufxF-0bqVR=j4jonS6N2Ka6hHUpFyu3_3TWKNhOW_4yFSSg@mail.gmail.com
---
src/bin/pg_dump/pg_dump.c | 22 +++++++++++-----------
src/bin/pg_dump/pg_dump.h | 4 ++--
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ea5e2abb03f..d5a12e675ce 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6118,8 +6118,8 @@ getTypes(Archive *fout)
/*
* If it's a domain, fetch info about its constraints, if any
*/
- tyinfo[i].nDomChecks = 0;
- tyinfo[i].domChecks = NULL;
+ tyinfo[i].nDomConstrs = 0;
+ tyinfo[i].domConstrs = NULL;
if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
tyinfo[i].typtype == TYPTYPE_DOMAIN)
getDomainConstraints(fout, &(tyinfo[i]));
@@ -8296,8 +8296,8 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
- tyinfo->nDomChecks = ntups;
- tyinfo->domChecks = constrinfo;
+ tyinfo->nDomConstrs = ntups;
+ tyinfo->domConstrs = constrinfo;
for (i = 0; i < ntups; i++)
{
@@ -12508,9 +12508,9 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
/*
* Add any CHECK, NOT NULL constraints for the domain
*/
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ for (i = 0; i < tyinfo->nDomConstrs; i++)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ ConstraintInfo *domcheck = &(tyinfo->domConstrs[i]);
if (!domcheck->separate)
appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
@@ -12554,19 +12554,19 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
NULL, tyinfo->rolname, &tyinfo->dacl);
/* Dump any per-constraint comments */
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ for (i = 0; i < tyinfo->nDomConstrs; i++)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ ConstraintInfo *domconstr = &(tyinfo->domConstrs[i]);
PQExpBuffer conprefix = createPQExpBuffer();
appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
- fmtId(domcheck->dobj.name));
+ fmtId(domconstr->dobj.name));
- if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
+ if (domconstr->dobj.dump & DUMP_COMPONENT_COMMENT)
dumpComment(fout, conprefix->data, qtypname,
tyinfo->dobj.namespace->dobj.name,
tyinfo->rolname,
- domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+ domconstr->dobj.catId, 0, tyinfo->dobj.dumpId);
destroyPQExpBuffer(conprefix);
}
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7417eab6aef..b5e19ecc2e3 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -223,8 +223,8 @@ typedef struct _typeInfo
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
/* If it's a domain, we store links to its constraints here: */
- int nDomChecks;
- struct _constraintInfo *domChecks;
+ int nDomConstrs;
+ struct _constraintInfo *domConstrs;
} TypeInfo;
typedef struct _shellTypeInfo
--
2.34.1
Ooh, you know what? I ran your the CREATE DOMAIN statement in your new
test and pg_dump'ed that, and as far as I can tell the _name_ of the
not-null constraint is not dumped either. So it's not just the comment
that we're losing. That's a separate bug, and probably needs to be
fixed first, although I'm not sure if that one is going to be
back-patchable. Which means, I withdraw my earlier assessment that the
comment fix is back-patchable as a whole.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
On Wed, May 7, 2025 at 10:56 PM Álvaro Herrera <alvherre@kurilemu.de> wrote:
Ooh, you know what? I ran your the CREATE DOMAIN statement in your new
test and pg_dump'ed that, and as far as I can tell the _name_ of the
not-null constraint is not dumped either. So it's not just the comment
that we're losing. That's a separate bug, and probably needs to be
fixed first, although I'm not sure if that one is going to be
back-patchable. Which means, I withdraw my earlier assessment that the
comment fix is back-patchable as a whole.
for PG17. we change the dump of
create domain test.d4 as int constraint nn not null;
from
CREATE DOMAIN test.d4 AS integer NOT NULL;
to
CREATE DOMAIN test.d4 AS integer CONSTRAINT nn NOT NULL;
in PG17 we didn't preserve the constraint name, now if we did, then
it's a bug fix,
so it can be back-patchable?
Anyway, the attachment is for PG18 only now, it will fix the domain constraint
name loss and domain not-null comments loss issue together.
Attachments:
v3-0002-sanitize-variable-or-argument-name-in-pg_dump.patchtext/x-patch; charset=US-ASCII; name=v3-0002-sanitize-variable-or-argument-name-in-pg_dump.patchDownload
From 2db869fc212867e74f61f4e5b2aef1fda017e9f7 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Thu, 8 May 2025 12:48:25 +0800
Subject: [PATCH v3 2/2] sanitize variable or argument name in pg_dump
discussion: https://postgr.es/m/CACJufxF-0bqVR=j4jonS6N2Ka6hHUpFyu3_3TWKNhOW_4yFSSg@mail.gmail.com
---
src/bin/pg_dump/pg_dump.c | 32 ++++++++++++++++----------------
src/bin/pg_dump/pg_dump.h | 4 ++--
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7837d4cea36..b3064f1b8d3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6118,8 +6118,8 @@ getTypes(Archive *fout)
/*
* If it's a domain, fetch info about its constraints, if any
*/
- tyinfo[i].nDomChecks = 0;
- tyinfo[i].domChecks = NULL;
+ tyinfo[i].nDomConstrs = 0;
+ tyinfo[i].domConstrs = NULL;
if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
tyinfo[i].typtype == TYPTYPE_DOMAIN)
getDomainConstraints(fout, &(tyinfo[i]));
@@ -8296,8 +8296,8 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
- tyinfo->nDomChecks = ntups;
- tyinfo->domChecks = constrinfo;
+ tyinfo->nDomConstrs = ntups;
+ tyinfo->domConstrs = constrinfo;
for (i = 0; i < ntups; i++)
{
@@ -12508,28 +12508,28 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
/*
* Add any CHECK, NOT NULL constraints for the domain
*/
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ for (i = 0; i < tyinfo->nDomConstrs; i++)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ ConstraintInfo *domconstr = &(tyinfo->domConstrs[i]);
- if (!domcheck->separate)
+ if (!domconstr->separate)
{
- if (domcheck->contype == 'n')
+ if (domconstr->contype == 'n')
{
char *default_name;
/* XXX should match ChooseConstraintName better */
default_name = psprintf("%s_not_null", qtypname);
- if (strcmp(default_name, fmtId(domcheck->dobj.name)) == 0)
+ if (strcmp(default_name, fmtId(domconstr->dobj.name)) == 0)
appendPQExpBufferStr(q, " NOT NULL");
else
appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
- fmtId(domcheck->dobj.name), domcheck->condef);
+ fmtId(domconstr->dobj.name), domconstr->condef);
}
else
appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
- fmtId(domcheck->dobj.name), domcheck->condef);
+ fmtId(domconstr->dobj.name), domconstr->condef);
}
}
@@ -12570,19 +12570,19 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
NULL, tyinfo->rolname, &tyinfo->dacl);
/* Dump any per-constraint comments */
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ for (i = 0; i < tyinfo->nDomConstrs; i++)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ ConstraintInfo *domconstr = &(tyinfo->domConstrs[i]);
PQExpBuffer conprefix = createPQExpBuffer();
appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
- fmtId(domcheck->dobj.name));
+ fmtId(domconstr->dobj.name));
- if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
+ if (domconstr->dobj.dump & DUMP_COMPONENT_COMMENT)
dumpComment(fout, conprefix->data, qtypname,
tyinfo->dobj.namespace->dobj.name,
tyinfo->rolname,
- domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+ domconstr->dobj.catId, 0, tyinfo->dobj.dumpId);
destroyPQExpBuffer(conprefix);
}
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7417eab6aef..b5e19ecc2e3 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -223,8 +223,8 @@ typedef struct _typeInfo
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
/* If it's a domain, we store links to its constraints here: */
- int nDomChecks;
- struct _constraintInfo *domChecks;
+ int nDomConstrs;
+ struct _constraintInfo *domConstrs;
} TypeInfo;
typedef struct _shellTypeInfo
--
2.34.1
v3-0001-Improve-pg_dump-handling-of-domain-not-null-constraints.patchtext/x-patch; charset=US-ASCII; name=v3-0001-Improve-pg_dump-handling-of-domain-not-null-constraints.patchDownload
From c6a2e754a5292b72a7e5e328ca4684f9c39517e8 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Thu, 8 May 2025 12:29:01 +0800
Subject: [PATCH v3 1/2] Improve pg_dump handling of domain not-null
constraints
1. If requested, we should dump comments for domain not-null constraints.
Note: In PostgreSQL 16 and earlier, these constraints do not have entries in
pg_constraint.
2. Similar to PG18 table not-null constraint, conditaionlly print out not-null
constraint name, This only apply to PG18.
discussion: https://postgr.es/m/CACJufxF-0bqVR=j4jonS6N2Ka6hHUpFyu3_3TWKNhOW_4yFSSg@mail.gmail.com
---
src/bin/pg_dump/pg_dump.c | 61 +++++++++++++++++++++++++-------
src/bin/pg_dump/pg_dump_sort.c | 9 ++---
src/bin/pg_dump/t/002_pg_dump.pl | 5 +++
3 files changed, 57 insertions(+), 18 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index e2e7975b34e..7837d4cea36 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -8250,7 +8250,8 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
int i_tableoid,
i_oid,
i_conname,
- i_consrc;
+ i_consrc,
+ i_contype;
int ntups;
if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
@@ -8260,10 +8261,19 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
"PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
"SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
- "convalidated "
+ "convalidated, "
+ "contype "
"FROM pg_catalog.pg_constraint "
- "WHERE contypid = $1 AND contype = 'c' "
- "ORDER BY conname");
+ "WHERE contypid = $1 ");
+
+ /*
+ * in PG18 or later versions, not-null domain constraint catalog
+ * information is stored in the pg_constraint.
+ */
+ if (fout->remoteVersion < 180000)
+ appendPQExpBufferStr(query, "AND contype = 'c' ");
+
+ appendPQExpBufferStr(query, "ORDER BY conname");
ExecuteSqlStatement(fout, query->data);
@@ -8282,6 +8292,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
i_oid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_consrc = PQfnumber(res, "consrc");
+ i_contype = PQfnumber(res, "contype");
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
@@ -8300,7 +8311,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
constrinfo[i].contable = NULL;
constrinfo[i].condomain = tyinfo;
- constrinfo[i].contype = 'c';
+ constrinfo[i].contype = *(PQgetvalue(res, i, i_contype));
constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
constrinfo[i].confrelid = InvalidOid;
constrinfo[i].conindex = 0;
@@ -12479,7 +12490,8 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
}
- if (typnotnull[0] == 't')
+ /* PG18 or later versions is handled in below */
+ if (typnotnull[0] == 't' && fout->remoteVersion < 180000)
appendPQExpBufferStr(q, " NOT NULL");
if (typdefault != NULL)
@@ -12494,15 +12506,31 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
PQclear(res);
/*
- * Add any CHECK constraints for the domain
+ * Add any CHECK, NOT NULL constraints for the domain
*/
for (i = 0; i < tyinfo->nDomChecks; i++)
{
ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
if (!domcheck->separate)
- appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
- fmtId(domcheck->dobj.name), domcheck->condef);
+ {
+ if (domcheck->contype == 'n')
+ {
+ char *default_name;
+
+ /* XXX should match ChooseConstraintName better */
+ default_name = psprintf("%s_not_null", qtypname);
+
+ if (strcmp(default_name, fmtId(domcheck->dobj.name)) == 0)
+ appendPQExpBufferStr(q, " NOT NULL");
+ else
+ appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
+ fmtId(domcheck->dobj.name), domcheck->condef);
+ }
+ else
+ appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
+ fmtId(domcheck->dobj.name), domcheck->condef);
+ }
}
appendPQExpBufferStr(q, ";\n");
@@ -18370,14 +18398,23 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
.dropStmt = delq->data));
}
}
- else if (coninfo->contype == 'c' && tbinfo == NULL)
+ else if (tbinfo == NULL)
{
- /* CHECK constraint on a domain */
+ /* CHECK, NOT NULL constraint on a domain */
TypeInfo *tyinfo = coninfo->condomain;
+ Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
+
/* Ignore if not to be dumped separately */
if (coninfo->separate)
{
+ const char *keyword;
+
+ if (coninfo->contype == 'c')
+ keyword = "CHECK CONSTRAINT";
+ else
+ keyword = "CONSTRAINT";
+
appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
fmtQualifiedDumpable(tyinfo));
appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
@@ -18396,7 +18433,7 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
ARCHIVE_OPTS(.tag = tag,
.namespace = tyinfo->dobj.namespace->dobj.name,
.owner = tyinfo->rolname,
- .description = "CHECK CONSTRAINT",
+ .description = keyword,
.section = SECTION_POST_DATA,
.createStmt = q->data,
.dropStmt = delq->data));
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 0b0977788f1..e5506ca788c 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -907,7 +907,7 @@ repairTableAttrDefMultiLoop(DumpableObject *tableobj,
}
/*
- * CHECK constraints on domains work just like those on tables ...
+ * CHECK, NOT NULL constraints on domains work just like those on tables ...
*/
static void
repairDomainConstraintLoop(DumpableObject *domainobj,
@@ -1173,11 +1173,10 @@ repairDependencyLoop(DumpableObject **loop,
}
}
- /* Domain and CHECK constraint */
+ /* Domain and CHECK, NOT NULL constraint */
if (nLoop == 2 &&
loop[0]->objType == DO_TYPE &&
loop[1]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[1])->contype == 'c' &&
((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
{
repairDomainConstraintLoop(loop[0], loop[1]);
@@ -1186,14 +1185,13 @@ repairDependencyLoop(DumpableObject **loop,
if (nLoop == 2 &&
loop[1]->objType == DO_TYPE &&
loop[0]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[0])->contype == 'c' &&
((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
{
repairDomainConstraintLoop(loop[1], loop[0]);
return;
}
- /* Indirect loop involving domain and CHECK constraint */
+ /* Indirect loop involving domain and CHECK OR NOT NULL constraint */
if (nLoop > 2)
{
for (i = 0; i < nLoop; i++)
@@ -1203,7 +1201,6 @@ repairDependencyLoop(DumpableObject **loop,
for (j = 0; j < nLoop; j++)
{
if (loop[j]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[j])->contype == 'c' &&
((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
{
repairDomainConstraintMultiLoop(loop[i], loop[j]);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 55d892d9c16..88369bc98ae 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2289,16 +2289,21 @@ my %tests = (
create_sql => 'CREATE DOMAIN dump_test.us_postal_code AS TEXT
COLLATE "C"
DEFAULT \'10014\'
+ CONSTRAINT nn NOT NULL
CHECK(VALUE ~ \'^\d{5}$\' OR
VALUE ~ \'^\d{5}-\d{4}$\');
+ COMMENT ON CONSTRAINT nn
+ ON DOMAIN dump_test.us_postal_code IS \'not null\';
COMMENT ON CONSTRAINT us_postal_code_check
ON DOMAIN dump_test.us_postal_code IS \'check it\';',
regexp => qr/^
\QCREATE DOMAIN dump_test.us_postal_code AS text COLLATE pg_catalog."C" DEFAULT '10014'::text\E\n\s+
+ \QCONSTRAINT nn NOT NULL \E
\QCONSTRAINT us_postal_code_check CHECK \E
\Q(((VALUE ~ '^\d{5}\E
\$\Q'::text) OR (VALUE ~ '^\d{5}-\d{4}\E\$
\Q'::text)));\E(.|\n)*
+ \QCOMMENT ON CONSTRAINT nn ON DOMAIN dump_test.us_postal_code IS 'not null';\E
\QCOMMENT ON CONSTRAINT us_postal_code_check ON DOMAIN dump_test.us_postal_code IS 'check it';\E
/xm,
like =>
--
2.34.1
hi.
"Catalog domain not-null constraints" commit[1]https://git.postgresql.org/cgit/postgresql.git/commit/?id=e5da0fe3c22b34c4433f1729e88495554b5331ed
doesn't have a pg_dump test.
I actually found another bug.
create schema test;
CREATE DOMAIN test.d1 AS integer NOT NULL default 11;
pg_dump --schema=test > 1.sql
""
pg_dump: warning: could not resolve dependency loop among these items:
pg_dump: detail: TYPE d1 (ID 1415 OID 18007)
pg_dump: detail: CONSTRAINT d1_not_null (ID 1416 OID 18008)
""
Catalog domain not-null constraints is committed in 17, so no need to
worry about before 17 branches.
The attached patch will work for PG17 and PG18.
You can use the following SQL examples
to compare with master and see the intended effect of my attached patch.
CREATE DOMAIN test.d1 AS integer NOT NULL default 11;
CREATE DOMAIN test.d3 AS integer constraint nn NOT NULL default 11;
[1]: https://git.postgresql.org/cgit/postgresql.git/commit/?id=e5da0fe3c22b34c4433f1729e88495554b5331ed
Attachments:
v4-0001-Improve-pg_dump-handling-of-domain-not-null-constraints.patchtext/x-patch; charset=US-ASCII; name=v4-0001-Improve-pg_dump-handling-of-domain-not-null-constraints.patchDownload
From aaa19222079d27c554c8b06a2e95b2f2581bccd8 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Thu, 22 May 2025 14:25:58 +0800
Subject: [PATCH v4 1/2] Improve pg_dump handling of domain not-null
constraints
1. If requested, we should dump comments for domain not-null constraints.
Note: In PostgreSQL 16 and earlier, these constraints do not have entries in
pg_constraint.
this patch can apply to PG17 and up.
discussion: https://postgr.es/m/CACJufxF-0bqVR=j4jonS6N2Ka6hHUpFyu3_3TWKNhOW_4yFSSg@mail.gmail.com
---
src/bin/pg_dump/pg_dump.c | 64 +++++++++++++++++++++++++++-----
src/bin/pg_dump/pg_dump_sort.c | 9 ++---
src/bin/pg_dump/t/002_pg_dump.pl | 6 ++-
3 files changed, 62 insertions(+), 17 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index c73e73a87d1..fe60ca874b3 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -8250,7 +8250,8 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
int i_tableoid,
i_oid,
i_conname,
- i_consrc;
+ i_consrc,
+ i_contype;
int ntups;
if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
@@ -8260,10 +8261,19 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
"PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
"SELECT tableoid, oid, conname, "
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
- "convalidated "
+ "convalidated, "
+ "contype "
"FROM pg_catalog.pg_constraint "
- "WHERE contypid = $1 AND contype = 'c' "
- "ORDER BY conname");
+ "WHERE contypid = $1 ");
+
+ /*
+ * only PG17 or later versions, not-null domain constraint catalog
+ * information is stored in the pg_constraint.
+ */
+ if (fout->remoteVersion < 170000)
+ appendPQExpBufferStr(query, "AND contype = 'c' ");
+
+ appendPQExpBufferStr(query, "ORDER BY conname");
ExecuteSqlStatement(fout, query->data);
@@ -8282,6 +8292,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
i_oid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_consrc = PQfnumber(res, "consrc");
+ i_contype = PQfnumber(res, "contype");
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
@@ -8300,7 +8311,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
constrinfo[i].contable = NULL;
constrinfo[i].condomain = tyinfo;
- constrinfo[i].contype = 'c';
+ constrinfo[i].contype = *(PQgetvalue(res, i, i_contype));
constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
constrinfo[i].confrelid = InvalidOid;
constrinfo[i].conindex = 0;
@@ -12485,7 +12496,31 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
}
if (typnotnull[0] == 't')
- appendPQExpBufferStr(q, " NOT NULL");
+ {
+ if (fout->remoteVersion < 170000)
+ appendPQExpBufferStr(q, " NOT NULL");
+ else
+ {
+ for (i = 0; i < tyinfo->nDomChecks; i++)
+ {
+ ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+
+ if (!domcheck->separate && domcheck->contype == 'n')
+ {
+ char *default_name;
+
+ /* XXX should match ChooseConstraintName better */
+ default_name = psprintf("%s_not_null", qtypname);
+
+ if (strcmp(default_name, fmtId(domcheck->dobj.name)) == 0)
+ appendPQExpBufferStr(q, " NOT NULL");
+ else
+ appendPQExpBuffer(q, " CONSTRAINT %s %s",
+ fmtId(domcheck->dobj.name), domcheck->condef);
+ }
+ }
+ }
+ }
if (typdefault != NULL)
{
@@ -12505,7 +12540,7 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
{
ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
- if (!domcheck->separate)
+ if (!domcheck->separate && domcheck->contype == 'c')
appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
fmtId(domcheck->dobj.name), domcheck->condef);
}
@@ -18375,14 +18410,23 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
.dropStmt = delq->data));
}
}
- else if (coninfo->contype == 'c' && tbinfo == NULL)
+ else if (tbinfo == NULL)
{
- /* CHECK constraint on a domain */
+ /* CHECK, NOT NULL constraint on a domain */
TypeInfo *tyinfo = coninfo->condomain;
+ Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
+
/* Ignore if not to be dumped separately */
if (coninfo->separate)
{
+ const char *keyword;
+
+ if (coninfo->contype == 'c')
+ keyword = "CHECK CONSTRAINT";
+ else
+ keyword = "CONSTRAINT";
+
appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
fmtQualifiedDumpable(tyinfo));
appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
@@ -18401,7 +18445,7 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
ARCHIVE_OPTS(.tag = tag,
.namespace = tyinfo->dobj.namespace->dobj.name,
.owner = tyinfo->rolname,
- .description = "CHECK CONSTRAINT",
+ .description = keyword,
.section = SECTION_POST_DATA,
.createStmt = q->data,
.dropStmt = delq->data));
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 0b0977788f1..e5506ca788c 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -907,7 +907,7 @@ repairTableAttrDefMultiLoop(DumpableObject *tableobj,
}
/*
- * CHECK constraints on domains work just like those on tables ...
+ * CHECK, NOT NULL constraints on domains work just like those on tables ...
*/
static void
repairDomainConstraintLoop(DumpableObject *domainobj,
@@ -1173,11 +1173,10 @@ repairDependencyLoop(DumpableObject **loop,
}
}
- /* Domain and CHECK constraint */
+ /* Domain and CHECK, NOT NULL constraint */
if (nLoop == 2 &&
loop[0]->objType == DO_TYPE &&
loop[1]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[1])->contype == 'c' &&
((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
{
repairDomainConstraintLoop(loop[0], loop[1]);
@@ -1186,14 +1185,13 @@ repairDependencyLoop(DumpableObject **loop,
if (nLoop == 2 &&
loop[1]->objType == DO_TYPE &&
loop[0]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[0])->contype == 'c' &&
((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
{
repairDomainConstraintLoop(loop[1], loop[0]);
return;
}
- /* Indirect loop involving domain and CHECK constraint */
+ /* Indirect loop involving domain and CHECK OR NOT NULL constraint */
if (nLoop > 2)
{
for (i = 0; i < nLoop; i++)
@@ -1203,7 +1201,6 @@ repairDependencyLoop(DumpableObject **loop,
for (j = 0; j < nLoop; j++)
{
if (loop[j]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[j])->contype == 'c' &&
((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
{
repairDomainConstraintMultiLoop(loop[i], loop[j]);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index cf34f71ea11..4786dace95e 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2289,16 +2289,20 @@ my %tests = (
create_sql => 'CREATE DOMAIN dump_test.us_postal_code AS TEXT
COLLATE "C"
DEFAULT \'10014\'
+ CONSTRAINT nn NOT NULL
CHECK(VALUE ~ \'^\d{5}$\' OR
VALUE ~ \'^\d{5}-\d{4}$\');
+ COMMENT ON CONSTRAINT nn
+ ON DOMAIN dump_test.us_postal_code IS \'not null\';
COMMENT ON CONSTRAINT us_postal_code_check
ON DOMAIN dump_test.us_postal_code IS \'check it\';',
regexp => qr/^
- \QCREATE DOMAIN dump_test.us_postal_code AS text COLLATE pg_catalog."C" DEFAULT '10014'::text\E\n\s+
+ \QCREATE DOMAIN dump_test.us_postal_code AS text COLLATE pg_catalog."C" CONSTRAINT nn NOT NULL DEFAULT '10014'::text\E\n\s+
\QCONSTRAINT us_postal_code_check CHECK \E
\Q(((VALUE ~ '^\d{5}\E
\$\Q'::text) OR (VALUE ~ '^\d{5}-\d{4}\E\$
\Q'::text)));\E(.|\n)*
+ \QCOMMENT ON CONSTRAINT nn ON DOMAIN dump_test.us_postal_code IS 'not null';\E(.|\n)*
\QCOMMENT ON CONSTRAINT us_postal_code_check ON DOMAIN dump_test.us_postal_code IS 'check it';\E
/xm,
like =>
--
2.34.1
v4-0002-sanitize-variable-or-argument-name-in-pg_dump.patchtext/x-patch; charset=US-ASCII; name=v4-0002-sanitize-variable-or-argument-name-in-pg_dump.patchDownload
From add8949e09407350f0af2ae49bb09c6de8af28f6 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Thu, 22 May 2025 14:21:10 +0800
Subject: [PATCH v4 2/2] sanitize variable or argument name in pg_dump
discussion: https://postgr.es/m/CACJufxF-0bqVR=j4jonS6N2Ka6hHUpFyu3_3TWKNhOW_4yFSSg@mail.gmail.com
---
src/bin/pg_dump/pg_dump.c | 36 ++++++++++++++++++------------------
src/bin/pg_dump/pg_dump.h | 4 ++--
2 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index fe60ca874b3..c8ee393c6ea 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -6118,8 +6118,8 @@ getTypes(Archive *fout)
/*
* If it's a domain, fetch info about its constraints, if any
*/
- tyinfo[i].nDomChecks = 0;
- tyinfo[i].domChecks = NULL;
+ tyinfo[i].nDomConstrs = 0;
+ tyinfo[i].domConstrs = NULL;
if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
tyinfo[i].typtype == TYPTYPE_DOMAIN)
getDomainConstraints(fout, &(tyinfo[i]));
@@ -8296,8 +8296,8 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
- tyinfo->nDomChecks = ntups;
- tyinfo->domChecks = constrinfo;
+ tyinfo->nDomConstrs = ntups;
+ tyinfo->domConstrs = constrinfo;
for (i = 0; i < ntups; i++)
{
@@ -12501,22 +12501,22 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
appendPQExpBufferStr(q, " NOT NULL");
else
{
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ for (i = 0; i < tyinfo->nDomConstrs; i++)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ ConstraintInfo *domconstr = &(tyinfo->domConstrs[i]);
- if (!domcheck->separate && domcheck->contype == 'n')
+ if (!domconstr->separate && domconstr->contype == 'n')
{
char *default_name;
/* XXX should match ChooseConstraintName better */
default_name = psprintf("%s_not_null", qtypname);
- if (strcmp(default_name, fmtId(domcheck->dobj.name)) == 0)
+ if (strcmp(default_name, fmtId(domconstr->dobj.name)) == 0)
appendPQExpBufferStr(q, " NOT NULL");
else
appendPQExpBuffer(q, " CONSTRAINT %s %s",
- fmtId(domcheck->dobj.name), domcheck->condef);
+ fmtId(domconstr->dobj.name), domconstr->condef);
}
}
}
@@ -12536,13 +12536,13 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
/*
* Add any CHECK constraints for the domain
*/
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ for (i = 0; i < tyinfo->nDomConstrs; i++)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ ConstraintInfo *domconstr = &(tyinfo->domConstrs[i]);
- if (!domcheck->separate && domcheck->contype == 'c')
+ if (!domconstr->separate && domconstr->contype == 'c')
appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
- fmtId(domcheck->dobj.name), domcheck->condef);
+ fmtId(domconstr->dobj.name), domconstr->condef);
}
appendPQExpBufferStr(q, ";\n");
@@ -12582,19 +12582,19 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
NULL, tyinfo->rolname, &tyinfo->dacl);
/* Dump any per-constraint comments */
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ for (i = 0; i < tyinfo->nDomConstrs; i++)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ ConstraintInfo *domconstr = &(tyinfo->domConstrs[i]);
PQExpBuffer conprefix = createPQExpBuffer();
appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
- fmtId(domcheck->dobj.name));
+ fmtId(domconstr->dobj.name));
- if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
+ if (domconstr->dobj.dump & DUMP_COMPONENT_COMMENT)
dumpComment(fout, conprefix->data, qtypname,
tyinfo->dobj.namespace->dobj.name,
tyinfo->rolname,
- domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+ domconstr->dobj.catId, 0, tyinfo->dobj.dumpId);
destroyPQExpBuffer(conprefix);
}
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 7417eab6aef..b5e19ecc2e3 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -223,8 +223,8 @@ typedef struct _typeInfo
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
/* If it's a domain, we store links to its constraints here: */
- int nDomChecks;
- struct _constraintInfo *domChecks;
+ int nDomConstrs;
+ struct _constraintInfo *domConstrs;
} TypeInfo;
typedef struct _shellTypeInfo
--
2.34.1
On 2025-May-22, jian he wrote:
I actually found another bug.
create schema test;
CREATE DOMAIN test.d1 AS integer NOT NULL default 11;
pg_dump --schema=test > 1.sql
""
pg_dump: warning: could not resolve dependency loop among these items:
pg_dump: detail: TYPE d1 (ID 1415 OID 18007)
pg_dump: detail: CONSTRAINT d1_not_null (ID 1416 OID 18008)
""
Oh, interesting. I agree with the rough fix, but I think it's better if
we keep the contype comparisons rather than removing them, relaxing to
allow for one more char.
I didn't like the idea of stashing the not-null constraint in the same
array as the check constraints; it feels a bit dirty (especially because
of the need to scan the array in order to find the not-null one). I
opted to add a separate TypeInfo->notnull pointer instead. This feels
more natural. This works because we know a domain has only one not-null
constraint. Note that this means we don't need your proposed 0002
anymore.
I wonder to what extent we promise ABI compatibility of pg_dump.h
structs in stable branches. If that's an issue, we could easily use
your original patch for 17, and use the TypeInfo->notnull addition only
in 18, but I'm starting to lean on not bothering (i.e., use the same
patch in all branches). Compare commit 7418767f1 which was backpatched
all the way and modified struct StatsExtInfo; I don't think we got any
complaints.
I also modified the Perl test so that the COMMENT ON CONSTRAINT
statement is checked in a separate stanza. This works fine: the comment
is created in the create_sql of one stanza (the same where you had it),
then checked in the 'regexp' entry of another. I opted to move the
regexp for both constraints out of the main one.
The attached applies on top of your patch. Opinions?
--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
Attachments:
0001-fix-tweak.patch.txttext/plain; charset=utf-8Download
From 5d29cbcee5ad62083f232db7fa0a3a7cf76455dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Mon, 14 Jul 2025 18:25:48 +0200
Subject: [PATCH] fix tweak
---
src/bin/pg_dump/pg_dump.c | 137 +++++++++++++++++++------------
src/bin/pg_dump/pg_dump.h | 4 +-
src/bin/pg_dump/pg_dump_sort.c | 10 ++-
src/bin/pg_dump/t/002_pg_dump.pl | 26 +++++-
4 files changed, 119 insertions(+), 58 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7849770fdee..2253f772fcc 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -47,6 +47,7 @@
#include "catalog/pg_authid_d.h"
#include "catalog/pg_cast_d.h"
#include "catalog/pg_class_d.h"
+#include "catalog/pg_constraint_d.h"
#include "catalog/pg_default_acl_d.h"
#include "catalog/pg_largeobject_d.h"
#include "catalog/pg_proc_d.h"
@@ -6122,6 +6123,7 @@ getTypes(Archive *fout)
*/
tyinfo[i].nDomChecks = 0;
tyinfo[i].domChecks = NULL;
+ tyinfo[i].notnull = NULL;
if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
tyinfo[i].typtype == TYPTYPE_DOMAIN)
getDomainConstraints(fout, &(tyinfo[i]));
@@ -8247,7 +8249,6 @@ addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
static void
getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
{
- int i;
ConstraintInfo *constrinfo;
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
@@ -8255,29 +8256,26 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
i_oid,
i_conname,
i_consrc,
+ i_convalidated,
i_contype;
int ntups;
if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
{
- /* Set up query for constraint-specific details */
- appendPQExpBufferStr(query,
- "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
- "SELECT tableoid, oid, conname, "
- "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
- "convalidated, "
- "contype "
- "FROM pg_catalog.pg_constraint "
- "WHERE contypid = $1 ");
-
/*
- * only PG17 or later versions, not-null domain constraint catalog
- * information is stored in the pg_constraint.
+ * Set up query for constraint-specific details. For servers 17 and
+ * up, domains have constraints of type 'n' as well as 'c', otherwise
+ * just the latter.
*/
- if (fout->remoteVersion < 170000)
- appendPQExpBufferStr(query, "AND contype = 'c' ");
-
- appendPQExpBufferStr(query, "ORDER BY conname");
+ appendPQExpBuffer(query,
+ "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
+ "SELECT tableoid, oid, conname, "
+ "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
+ "convalidated, contype "
+ "FROM pg_catalog.pg_constraint "
+ "WHERE contypid = $1 AND contype IN (%s) "
+ "ORDER BY conname",
+ fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
ExecuteSqlStatement(fout, query->data);
@@ -8296,34 +8294,49 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
i_oid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_consrc = PQfnumber(res, "consrc");
+ i_convalidated = PQfnumber(res, "convalidated");
i_contype = PQfnumber(res, "contype");
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
-
- tyinfo->nDomChecks = ntups;
tyinfo->domChecks = constrinfo;
- for (i = 0; i < ntups; i++)
+ for (int i = 0, j = 0; i < ntups; i++)
{
- bool validated = PQgetvalue(res, i, 4)[0] == 't';
+ bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
+ char contype = (PQgetvalue(res, i, i_contype))[0];
+ ConstraintInfo *constraint;
- constrinfo[i].dobj.objType = DO_CONSTRAINT;
- constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
- constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
- AssignDumpId(&constrinfo[i].dobj);
- constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
- constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
- constrinfo[i].contable = NULL;
- constrinfo[i].condomain = tyinfo;
- constrinfo[i].contype = *(PQgetvalue(res, i, i_contype));
- constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
- constrinfo[i].confrelid = InvalidOid;
- constrinfo[i].conindex = 0;
- constrinfo[i].condeferrable = false;
- constrinfo[i].condeferred = false;
- constrinfo[i].conislocal = true;
+ if (contype == CONSTRAINT_CHECK)
+ {
+ constraint = &constrinfo[j++];
+ tyinfo->nDomChecks++;
+ }
+ else
+ {
+ Assert(contype == CONSTRAINT_NOTNULL);
+ Assert(tyinfo->notnull == NULL);
+ /* use last item in array for the not-null constraint */
+ tyinfo->notnull = &(constrinfo[ntups - 1]);
+ constraint = tyinfo->notnull;
+ }
- constrinfo[i].separate = !validated;
+ constraint->dobj.objType = DO_CONSTRAINT;
+ constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&(constraint->dobj));
+ constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
+ constraint->dobj.namespace = tyinfo->dobj.namespace;
+ constraint->contable = NULL;
+ constraint->condomain = tyinfo;
+ constraint->contype = *(PQgetvalue(res, i, i_contype));
+ constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
+ constraint->confrelid = InvalidOid;
+ constraint->conindex = 0;
+ constraint->condeferrable = false;
+ constraint->condeferred = false;
+ constraint->conislocal = true;
+
+ constraint->separate = !validated;
/*
* Make the domain depend on the constraint, ensuring it won't be
@@ -8332,8 +8345,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
* anyway, so this doesn't matter.
*/
if (validated)
- addObjectDependency(&tyinfo->dobj,
- constrinfo[i].dobj.dumpId);
+ addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
}
PQclear(res);
@@ -12528,29 +12540,32 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
}
+ /*
+ * Print a not-null constraint if there's one. In servers older than 17
+ * these don't have names, so just print it unadorned; in newer ones they
+ * do, but most of the time it's going to be the standard generated one,
+ * so omit the name in that case also.
+ */
if (typnotnull[0] == 't')
{
if (fout->remoteVersion < 170000)
appendPQExpBufferStr(q, " NOT NULL");
else
{
- for (i = 0; i < tyinfo->nDomChecks; i++)
+ ConstraintInfo *notnull = tyinfo->notnull;
+
+ if (!notnull->separate)
{
- ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
+ char *default_name;
- if (!domcheck->separate && domcheck->contype == 'n')
- {
- char *default_name;
+ /* XXX should match ChooseConstraintName better */
+ default_name = psprintf("%s_not_null", qtypname);
- /* XXX should match ChooseConstraintName better */
- default_name = psprintf("%s_not_null", qtypname);
-
- if (strcmp(default_name, fmtId(domcheck->dobj.name)) == 0)
- appendPQExpBufferStr(q, " NOT NULL");
- else
- appendPQExpBuffer(q, " CONSTRAINT %s %s",
- fmtId(domcheck->dobj.name), domcheck->condef);
- }
+ if (strcmp(default_name, fmtId(notnull->dobj.name)) == 0)
+ appendPQExpBufferStr(q, " NOT NULL");
+ else
+ appendPQExpBuffer(q, " CONSTRAINT %s %s",
+ fmtId(notnull->dobj.name), notnull->condef);
}
}
}
@@ -12632,6 +12647,22 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
destroyPQExpBuffer(conprefix);
}
+ /* And a comment on the not-null constraint, if there's one */
+ if (tyinfo->notnull != NULL)
+ {
+ PQExpBuffer conprefix = createPQExpBuffer();
+
+ appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
+ fmtId(tyinfo->notnull->dobj.name));
+
+ if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, conprefix->data, qtypname,
+ tyinfo->dobj.namespace->dobj.name,
+ tyinfo->rolname,
+ tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
+ destroyPQExpBuffer(conprefix);
+ }
+
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
destroyPQExpBuffer(query);
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 39eef1d6617..2370c98d192 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -222,7 +222,9 @@ typedef struct _typeInfo
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
- /* If it's a domain, we store links to its constraints here: */
+ /* If it's a domain, its not-null constraint is here: */
+ struct _constraintInfo *notnull;
+ /* If it's a domain, we store links to its CHECK constraints here: */
int nDomChecks;
struct _constraintInfo *domChecks;
} TypeInfo;
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index d3eec6b2598..7b42f4882cc 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -1173,10 +1173,12 @@ repairDependencyLoop(DumpableObject **loop,
}
}
- /* Domain and CHECK, NOT NULL constraint */
+ /* Domain and CHECK or NOT NULL constraint */
if (nLoop == 2 &&
loop[0]->objType == DO_TYPE &&
loop[1]->objType == DO_CONSTRAINT &&
+ (((ConstraintInfo *) loop[1])->contype == 'c' ||
+ ((ConstraintInfo *) loop[1])->contype == 'n') &&
((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
{
repairDomainConstraintLoop(loop[0], loop[1]);
@@ -1185,13 +1187,15 @@ repairDependencyLoop(DumpableObject **loop,
if (nLoop == 2 &&
loop[1]->objType == DO_TYPE &&
loop[0]->objType == DO_CONSTRAINT &&
+ (((ConstraintInfo *) loop[1])->contype == 'c' ||
+ ((ConstraintInfo *) loop[1])->contype == 'n') &&
((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
{
repairDomainConstraintLoop(loop[1], loop[0]);
return;
}
- /* Indirect loop involving domain and CHECK OR NOT NULL constraint */
+ /* Indirect loop involving domain and CHECK or NOT NULL constraint */
if (nLoop > 2)
{
for (i = 0; i < nLoop; i++)
@@ -1201,6 +1205,8 @@ repairDependencyLoop(DumpableObject **loop,
for (j = 0; j < nLoop; j++)
{
if (loop[j]->objType == DO_CONSTRAINT &&
+ (((ConstraintInfo *) loop[1])->contype == 'c' ||
+ ((ConstraintInfo *) loop[1])->contype == 'n') &&
((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
{
repairDomainConstraintMultiLoop(loop[i], loop[j]);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 90d1067f3e2..771cdcecb60 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2390,8 +2390,6 @@ my %tests = (
\Q(((VALUE ~ '^\d{5}\E
\$\Q'::text) OR (VALUE ~ '^\d{5}-\d{4}\E\$
\Q'::text)));\E(.|\n)*
- \QCOMMENT ON CONSTRAINT nn ON DOMAIN dump_test.us_postal_code IS 'not null';\E(.|\n)*
- \QCOMMENT ON CONSTRAINT us_postal_code_check ON DOMAIN dump_test.us_postal_code IS 'check it';\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
@@ -2401,6 +2399,30 @@ my %tests = (
},
},
+ 'COMMENT ON CONSTRAINT ON DOMAIN (1)' => {
+ regexp => qr/^
+ \QCOMMENT ON CONSTRAINT nn ON DOMAIN dump_test.us_postal_code IS 'not null';\E
+ /xm,
+ like =>
+ { %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_measurement => 1,
+ },
+ },
+
+ 'COMMENT ON CONSTRAINT ON DOMAIN (2)' => {
+ regexp => qr/^
+ \QCOMMENT ON CONSTRAINT us_postal_code_check ON DOMAIN dump_test.us_postal_code IS 'check it';\E
+ /xm,
+ like =>
+ { %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_measurement => 1,
+ },
+ },
+
'CREATE FUNCTION dump_test.pltestlang_call_handler' => {
create_order => 17,
create_sql => 'CREATE FUNCTION dump_test.pltestlang_call_handler()
--
2.39.5
=?utf-8?Q?=C3=81lvaro?= Herrera <alvherre@kurilemu.de> writes:
I wonder to what extent we promise ABI compatibility of pg_dump.h
structs in stable branches.
Those should be private to pg_dump, so I see no objection to changing
them.
regards, tom lane
On Tue, Jul 15, 2025 at 2:24 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
On 2025-May-22, jian he wrote:
I actually found another bug.
create schema test;
CREATE DOMAIN test.d1 AS integer NOT NULL default 11;
pg_dump --schema=test > 1.sql
""
pg_dump: warning: could not resolve dependency loop among these items:
pg_dump: detail: TYPE d1 (ID 1415 OID 18007)
pg_dump: detail: CONSTRAINT d1_not_null (ID 1416 OID 18008)
""Oh, interesting. I agree with the rough fix, but I think it's better if
we keep the contype comparisons rather than removing them, relaxing to
allow for one more char.I didn't like the idea of stashing the not-null constraint in the same
array as the check constraints; it feels a bit dirty (especially because
of the need to scan the array in order to find the not-null one). I
opted to add a separate TypeInfo->notnull pointer instead. This feels
more natural. This works because we know a domain has only one not-null
constraint. Note that this means we don't need your proposed 0002
anymore.
TypeInfo->notnull is much better than
TypeInfo->domChecks handle both check and not-null constraints.
The attached applies on top of your patch. Opinions?
+ constraint->contype = *(PQgetvalue(res, i, i_contype));
can change to
constraint->contype = contype;
in getDomainConstraints, we use pg_malloc
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
after
constraint->condeferred = false;
would better also add
constraint->conperiod = false;
accidently found another existing bug.
create schema test;
CREATE DOMAIN test.d1 AS integer NOT NULL default 11;
alter domain test.d1 add constraint a2 check(value > 1) not valid;
comment on CONSTRAINT a2 ON DOMAIN test.d1 is 'test';
dump output is:
CREATE SCHEMA test;
CREATE DOMAIN test.d1 AS integer NOT NULL DEFAULT 11;
COMMENT ON CONSTRAINT a2 ON DOMAIN test.d1 IS 'test';
ALTER DOMAIN test.d1
ADD CONSTRAINT a2 CHECK ((VALUE > 1)) NOT VALID;
Obviously the COMMENT command will error out.
currently working on a fix, just sharing the bug details in advance.
On Tue, Jul 15, 2025 at 2:10 PM jian he <jian.universality@gmail.com> wrote:
accidently found another existing bug.
create schema test;
CREATE DOMAIN test.d1 AS integer NOT NULL default 11;
alter domain test.d1 add constraint a2 check(value > 1) not valid;
comment on CONSTRAINT a2 ON DOMAIN test.d1 is 'test';
dump output is:CREATE SCHEMA test;
CREATE DOMAIN test.d1 AS integer NOT NULL DEFAULT 11;
COMMENT ON CONSTRAINT a2 ON DOMAIN test.d1 IS 'test';
ALTER DOMAIN test.d1
ADD CONSTRAINT a2 CHECK ((VALUE > 1)) NOT VALID;Obviously the COMMENT command will error out.
currently working on a fix, just sharing the bug details in advance.
we should let:
dumpConstraint handles dumping separate "NOT VALID" domain constraints along
with their comments.
dumpDomain: handles dumping "inlined" valid (not separate) domain constraints
together with their comments.
tested locally, i didn't write the test on src/bin/pg_dump/t/002_pg_dump.pl....
v5-0001-fix-pg_dump-domain-not-valid-constraint-comments.no-cfbot
was based on
v4-0001-Improve-pg_dump-handling-of-domain-not-null-constraints.patch
and 0001-fix-tweak.patch.txt.
Attachments:
v5-0001-fix-pg_dump-domain-not-valid-constraint-comments.no-cfbotapplication/octet-stream; name=v5-0001-fix-pg_dump-domain-not-valid-constraint-comments.no-cfbotDownload
From 3e5d1deac3096b2e3b0a465d8eba7b34cd72363d Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Tue, 15 Jul 2025 15:39:39 +0800
Subject: [PATCH v5 1/1] fix pg_dump domain not valid constraint comments
comments on a domain's "NOT VALID" constraint should be dumped only after the
constraint's definition has been dumped.
So overall:
dumpConstraint: handles dumping separate "NOT VALID" domain constraints along
with their comments.
dumpDomain: handles dumping "inlined" valid (not separate) domain constraints
together with their comments.
---
src/bin/pg_dump/pg_dump.c | 40 +++++++++++++++++++++++++++++----------
1 file changed, 30 insertions(+), 10 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 2253f772fcc..f9cb9339712 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12633,22 +12633,25 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
for (i = 0; i < tyinfo->nDomChecks; i++)
{
ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
- PQExpBuffer conprefix = createPQExpBuffer();
+ if (!domcheck->separate)
+ {
+ PQExpBuffer conprefix = createPQExpBuffer();
- appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
- fmtId(domcheck->dobj.name));
+ appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
+ fmtId(domcheck->dobj.name));
- if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
- dumpComment(fout, conprefix->data, qtypname,
- tyinfo->dobj.namespace->dobj.name,
- tyinfo->rolname,
- domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
+ if (domcheck->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, conprefix->data, qtypname,
+ tyinfo->dobj.namespace->dobj.name,
+ tyinfo->rolname,
+ domcheck->dobj.catId, 0, tyinfo->dobj.dumpId);
- destroyPQExpBuffer(conprefix);
+ destroyPQExpBuffer(conprefix);
+ }
}
/* And a comment on the not-null constraint, if there's one */
- if (tyinfo->notnull != NULL)
+ if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
{
PQExpBuffer conprefix = createPQExpBuffer();
@@ -18563,6 +18566,23 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
.section = SECTION_POST_DATA,
.createStmt = q->data,
.dropStmt = delq->data));
+
+ if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ {
+ char *qtypname;
+
+ PQExpBuffer conprefix = createPQExpBuffer();
+ qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
+
+ appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
+ fmtId(coninfo->dobj.name));
+
+ dumpComment(fout, conprefix->data, qtypname,
+ tyinfo->dobj.namespace->dobj.name,
+ tyinfo->rolname,
+ coninfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ destroyPQExpBuffer(conprefix);
+ }
}
}
else
--
2.34.1
On 2025-Jul-15, jian he wrote:
On Tue, Jul 15, 2025 at 2:10 PM jian he <jian.universality@gmail.com> wrote:
accidently found another existing bug.
CREATE SCHEMA test;
CREATE DOMAIN test.d1 AS integer NOT NULL DEFAULT 11;
COMMENT ON CONSTRAINT a2 ON DOMAIN test.d1 IS 'test';
ALTER DOMAIN test.d1
ADD CONSTRAINT a2 CHECK ((VALUE > 1)) NOT VALID;Obviously the COMMENT command will error out.
currently working on a fix, just sharing the bug details in advance.
Ouch, ugh. I think this is as old as NOT VALID constraints on domains,
maybe from commit 897795240cfa (Jun 2011), or ... no, actually
7eca575d1c28 (Dec 2014) which enabled constraints on domains to have
comments. In either case it's my fault, and the fix needs to be
backpatched all the way back, so I'm going to apply this bugfix one
ahead of the others, which are for newer bugs.
we should let:
dumpConstraint handles dumping separate "NOT VALID" domain constraints along
with their comments.
dumpDomain: handles dumping "inlined" valid (not separate) domain constraints
together with their comments.
Yeah, this makes sense.
tested locally, i didn't write the test on src/bin/pg_dump/t/002_pg_dump.pl....
I'll add something for coverage, but not yet sure if it's going to be
something in 002_pg_dump.pl, or objects to be left around for the
pg_upgrade test to pick up.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/
Attachments:
0001-Fix-dumping-of-comments-on-invalid-constraints-on-do.patchtext/x-diff; charset=utf-8Download
From fa407dccc9782d8f3accdcf4045ab9bb8859aef9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Tue, 15 Jul 2025 15:24:33 +0200
Subject: [PATCH] Fix dumping of comments on invalid constraints on domains
We skip dumping constraints together with domains if they are invalid
('separate') so that they appear after data -- but their comments were
dumped together with the domain definition, which in effect leads to the
comment being dumped when the constraint does not yet exist. Delay
them in the same way.
Oversight in 7eca575d1c28; backpatch all the way back.
Author: jian he <jian.universality@gmail.com>
Discussion: https://postgr.es/m/CACJufxF_C2pe6J_+nPr6C5jf5rQnbYP8XOKr4HM8yHZtp2aQqQ@mail.gmail.com
---
src/bin/pg_dump/pg_dump.c | 25 ++++++++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 1937997ea67..5af2ac151c2 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12582,9 +12582,15 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
/* Dump any per-constraint comments */
for (i = 0; i < tyinfo->nDomChecks; i++)
{
ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
- PQExpBuffer conprefix = createPQExpBuffer();
+ PQExpBuffer conprefix;
+
+ /* but only if the constraint itself was dumped here */
+ if (domcheck->separate)
+ continue;
+
+ conprefix = createPQExpBuffer();
appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
fmtId(domcheck->dobj.name));
@@ -18487,8 +18493,25 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
.description = "CHECK CONSTRAINT",
.section = SECTION_POST_DATA,
.createStmt = q->data,
.dropStmt = delq->data));
+
+ if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT)
+ {
+ char *qtypname;
+
+ PQExpBuffer conprefix = createPQExpBuffer();
+ qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
+
+ appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
+ fmtId(coninfo->dobj.name));
+
+ dumpComment(fout, conprefix->data, qtypname,
+ tyinfo->dobj.namespace->dobj.name,
+ tyinfo->rolname,
+ coninfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ destroyPQExpBuffer(conprefix);
+ }
}
}
else
{
--
2.39.5
On 2025-Jul-15, jian he wrote:
we should let:
dumpConstraint handles dumping separate "NOT VALID" domain constraints along
with their comments.
dumpDomain: handles dumping "inlined" valid (not separate) domain constraints
together with their comments.
Hmm, okay, but not-null constraints on domain cannot be NOT VALID at
present. I think it would be a useful feature (so that users can make
domains not-null that are used in tables already populated with data),
but first you'd need to implement things so that each table has a
not-null constraint row for the column whose type is the domain that's
being marked not-null.
Anyway, here's a patch.
--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
Attachments:
v6-0001-pg_dump-include-comments-on-not-null-constraints-.patchtext/x-diff; charset=utf-8Download
From b5aada261e675299529822d39a1ec165c1880a7f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81lvaro=20Herrera?= <alvherre@kurilemu.de>
Date: Thu, 17 Jul 2025 18:46:57 +0200
Subject: [PATCH v6] pg_dump: include comments on not-null constraints on
domains, too
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Commit e5da0fe3c22b introduced catalog entries for not-null constraints
on domains; but because commit b0e96f311985 (the original work for
catalogued not-null constraints on tables) forgot to teach pg_dump to
process the comments for them, this one also forgot. Add that now.
Backpatch-through: 17
Co-authored-by: jian he <jian.universality@gmail.com>
Co-authored-by: Ãlvaro Herrera <alvherre@kurilemu.de>
Reported-by: jian he <jian.universality@gmail.com>
Discussion: https://postgr.es/m/CACJufxF-0bqVR=j4jonS6N2Ka6hHUpFyu3_3TWKNhOW_4yFSSg@mail.gmail.com
---
src/bin/pg_dump/pg_dump.c | 154 +++++++++++++++++++++++--------
src/bin/pg_dump/pg_dump.h | 4 +-
src/bin/pg_dump/pg_dump_sort.c | 15 +--
src/bin/pg_dump/t/002_pg_dump.pl | 30 +++++-
4 files changed, 156 insertions(+), 47 deletions(-)
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 06f630a910d..a24048e1566 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -47,6 +47,7 @@
#include "catalog/pg_authid_d.h"
#include "catalog/pg_cast_d.h"
#include "catalog/pg_class_d.h"
+#include "catalog/pg_constraint_d.h"
#include "catalog/pg_default_acl_d.h"
#include "catalog/pg_largeobject_d.h"
#include "catalog/pg_largeobject_metadata_d.h"
@@ -5929,6 +5930,7 @@ getTypes(Archive *fout, int *numTypes)
*/
tyinfo[i].nDomChecks = 0;
tyinfo[i].domChecks = NULL;
+ tyinfo[i].notnull = NULL;
if ((tyinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
tyinfo[i].typtype == TYPTYPE_DOMAIN)
getDomainConstraints(fout, &(tyinfo[i]));
@@ -7949,27 +7951,33 @@ addConstrChildIdxDeps(DumpableObject *dobj, const IndxInfo *refidx)
static void
getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
{
- int i;
ConstraintInfo *constrinfo;
PQExpBuffer query = createPQExpBuffer();
PGresult *res;
int i_tableoid,
i_oid,
i_conname,
- i_consrc;
+ i_consrc,
+ i_convalidated,
+ i_contype;
int ntups;
if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
{
- /* Set up query for constraint-specific details */
- appendPQExpBufferStr(query,
- "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
- "SELECT tableoid, oid, conname, "
- "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
- "convalidated "
- "FROM pg_catalog.pg_constraint "
- "WHERE contypid = $1 AND contype = 'c' "
- "ORDER BY conname");
+ /*
+ * Set up query for constraint-specific details. For servers 17 and
+ * up, domains have constraints of type 'n' as well as 'c', otherwise
+ * just the latter.
+ */
+ appendPQExpBuffer(query,
+ "PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
+ "SELECT tableoid, oid, conname, "
+ "pg_catalog.pg_get_constraintdef(oid) AS consrc, "
+ "convalidated, contype "
+ "FROM pg_catalog.pg_constraint "
+ "WHERE contypid = $1 AND contype IN (%s) "
+ "ORDER BY conname",
+ fout->remoteVersion < 170000 ? "'c'" : "'c', 'n'");
ExecuteSqlStatement(fout, query->data);
@@ -7988,33 +7996,49 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
i_oid = PQfnumber(res, "oid");
i_conname = PQfnumber(res, "conname");
i_consrc = PQfnumber(res, "consrc");
+ i_convalidated = PQfnumber(res, "convalidated");
+ i_contype = PQfnumber(res, "contype");
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
-
- tyinfo->nDomChecks = ntups;
tyinfo->domChecks = constrinfo;
- for (i = 0; i < ntups; i++)
+ for (int i = 0, j = 0; i < ntups; i++)
{
- bool validated = PQgetvalue(res, i, 4)[0] == 't';
+ bool validated = PQgetvalue(res, i, i_convalidated)[0] == 't';
+ char contype = (PQgetvalue(res, i, i_contype))[0];
+ ConstraintInfo *constraint;
- constrinfo[i].dobj.objType = DO_CONSTRAINT;
- constrinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
- constrinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
- AssignDumpId(&constrinfo[i].dobj);
- constrinfo[i].dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
- constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
- constrinfo[i].contable = NULL;
- constrinfo[i].condomain = tyinfo;
- constrinfo[i].contype = 'c';
- constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
- constrinfo[i].confrelid = InvalidOid;
- constrinfo[i].conindex = 0;
- constrinfo[i].condeferrable = false;
- constrinfo[i].condeferred = false;
- constrinfo[i].conislocal = true;
+ if (contype == CONSTRAINT_CHECK)
+ {
+ constraint = &constrinfo[j++];
+ tyinfo->nDomChecks++;
+ }
+ else
+ {
+ Assert(contype == CONSTRAINT_NOTNULL);
+ Assert(tyinfo->notnull == NULL);
+ /* use last item in array for the not-null constraint */
+ tyinfo->notnull = &(constrinfo[ntups - 1]);
+ constraint = tyinfo->notnull;
+ }
- constrinfo[i].separate = !validated;
+ constraint->dobj.objType = DO_CONSTRAINT;
+ constraint->dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
+ constraint->dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
+ AssignDumpId(&(constraint->dobj));
+ constraint->dobj.name = pg_strdup(PQgetvalue(res, i, i_conname));
+ constraint->dobj.namespace = tyinfo->dobj.namespace;
+ constraint->contable = NULL;
+ constraint->condomain = tyinfo;
+ constraint->contype = *(PQgetvalue(res, i, i_contype));
+ constraint->condef = pg_strdup(PQgetvalue(res, i, i_consrc));
+ constraint->confrelid = InvalidOid;
+ constraint->conindex = 0;
+ constraint->condeferrable = false;
+ constraint->condeferred = false;
+ constraint->conislocal = true;
+
+ constraint->separate = !validated;
/*
* Make the domain depend on the constraint, ensuring it won't be
@@ -8023,8 +8047,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
* anyway, so this doesn't matter.
*/
if (validated)
- addObjectDependency(&tyinfo->dobj,
- constrinfo[i].dobj.dumpId);
+ addObjectDependency(&tyinfo->dobj, constraint->dobj.dumpId);
}
PQclear(res);
@@ -11557,8 +11580,35 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
}
+ /*
+ * Print a not-null constraint if there's one. In servers older than 17
+ * these don't have names, so just print it unadorned; in newer ones they
+ * do, but most of the time it's going to be the standard generated one,
+ * so omit the name in that case also.
+ */
if (typnotnull[0] == 't')
- appendPQExpBufferStr(q, " NOT NULL");
+ {
+ if (fout->remoteVersion < 170000)
+ appendPQExpBufferStr(q, " NOT NULL");
+ else
+ {
+ ConstraintInfo *notnull = tyinfo->notnull;
+
+ if (!notnull->separate)
+ {
+ char *default_name;
+
+ /* XXX should match ChooseConstraintName better */
+ default_name = psprintf("%s_not_null", qtypname);
+
+ if (strcmp(default_name, fmtId(notnull->dobj.name)) == 0)
+ appendPQExpBufferStr(q, " NOT NULL");
+ else
+ appendPQExpBuffer(q, " CONSTRAINT %s %s",
+ fmtId(notnull->dobj.name), notnull->condef);
+ }
+ }
+ }
if (typdefault != NULL)
{
@@ -11578,7 +11628,7 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
{
ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
- if (!domcheck->separate)
+ if (!domcheck->separate && domcheck->contype == 'c')
appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
fmtId(domcheck->dobj.name), domcheck->condef);
}
@@ -11642,6 +11692,25 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
destroyPQExpBuffer(conprefix);
}
+ /*
+ * And a comment on the not-null constraint, if there's one -- but only if
+ * the constraint itself was dumped here
+ */
+ if (tyinfo->notnull != NULL && !tyinfo->notnull->separate)
+ {
+ PQExpBuffer conprefix = createPQExpBuffer();
+
+ appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN",
+ fmtId(tyinfo->notnull->dobj.name));
+
+ if (tyinfo->notnull->dobj.dump & DUMP_COMPONENT_COMMENT)
+ dumpComment(fout, conprefix->data, qtypname,
+ tyinfo->dobj.namespace->dobj.name,
+ tyinfo->rolname,
+ tyinfo->notnull->dobj.catId, 0, tyinfo->dobj.dumpId);
+ destroyPQExpBuffer(conprefix);
+ }
+
destroyPQExpBuffer(q);
destroyPQExpBuffer(delq);
destroyPQExpBuffer(query);
@@ -17336,14 +17405,23 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
.dropStmt = delq->data));
}
}
- else if (coninfo->contype == 'c' && tbinfo == NULL)
+ else if (tbinfo == NULL)
{
- /* CHECK constraint on a domain */
+ /* CHECK, NOT NULL constraint on a domain */
TypeInfo *tyinfo = coninfo->condomain;
+ Assert(coninfo->contype == 'c' || coninfo->contype == 'n');
+
/* Ignore if not to be dumped separately */
if (coninfo->separate)
{
+ const char *keyword;
+
+ if (coninfo->contype == 'c')
+ keyword = "CHECK CONSTRAINT";
+ else
+ keyword = "CONSTRAINT";
+
appendPQExpBuffer(q, "ALTER DOMAIN %s\n",
fmtQualifiedDumpable(tyinfo));
appendPQExpBuffer(q, " ADD CONSTRAINT %s %s;\n",
@@ -17362,7 +17440,7 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
ARCHIVE_OPTS(.tag = tag,
.namespace = tyinfo->dobj.namespace->dobj.name,
.owner = tyinfo->rolname,
- .description = "CHECK CONSTRAINT",
+ .description = keyword,
.section = SECTION_POST_DATA,
.createStmt = q->data,
.dropStmt = delq->data));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 1d352fe12d1..243942369ea 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -215,7 +215,9 @@ typedef struct _typeInfo
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */
- /* If it's a domain, we store links to its constraints here: */
+ /* If it's a domain, its not-null constraint is here: */
+ struct _constraintInfo *notnull;
+ /* If it's a domain, we store links to its CHECK constraints here: */
int nDomChecks;
struct _constraintInfo *domChecks;
} TypeInfo;
diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c
index 31bdb91a585..cd846e44234 100644
--- a/src/bin/pg_dump/pg_dump_sort.c
+++ b/src/bin/pg_dump/pg_dump_sort.c
@@ -884,7 +884,7 @@ repairTableAttrDefMultiLoop(DumpableObject *tableobj,
}
/*
- * CHECK constraints on domains work just like those on tables ...
+ * CHECK, NOT NULL constraints on domains work just like those on tables ...
*/
static void
repairDomainConstraintLoop(DumpableObject *domainobj,
@@ -1135,11 +1135,12 @@ repairDependencyLoop(DumpableObject **loop,
}
}
- /* Domain and CHECK constraint */
+ /* Domain and CHECK or NOT NULL constraint */
if (nLoop == 2 &&
loop[0]->objType == DO_TYPE &&
loop[1]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[1])->contype == 'c' &&
+ (((ConstraintInfo *) loop[1])->contype == 'c' ||
+ ((ConstraintInfo *) loop[1])->contype == 'n') &&
((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
{
repairDomainConstraintLoop(loop[0], loop[1]);
@@ -1148,14 +1149,15 @@ repairDependencyLoop(DumpableObject **loop,
if (nLoop == 2 &&
loop[1]->objType == DO_TYPE &&
loop[0]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[0])->contype == 'c' &&
+ (((ConstraintInfo *) loop[1])->contype == 'c' ||
+ ((ConstraintInfo *) loop[1])->contype == 'n') &&
((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
{
repairDomainConstraintLoop(loop[1], loop[0]);
return;
}
- /* Indirect loop involving domain and CHECK constraint */
+ /* Indirect loop involving domain and CHECK or NOT NULL constraint */
if (nLoop > 2)
{
for (i = 0; i < nLoop; i++)
@@ -1165,7 +1167,8 @@ repairDependencyLoop(DumpableObject **loop,
for (j = 0; j < nLoop; j++)
{
if (loop[j]->objType == DO_CONSTRAINT &&
- ((ConstraintInfo *) loop[j])->contype == 'c' &&
+ (((ConstraintInfo *) loop[1])->contype == 'c' ||
+ ((ConstraintInfo *) loop[1])->contype == 'n') &&
((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
{
repairDomainConstraintMultiLoop(loop[i], loop[j]);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index 5bcc2244d58..58306d307ec 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -2041,17 +2041,19 @@ my %tests = (
create_sql => 'CREATE DOMAIN dump_test.us_postal_code AS TEXT
COLLATE "C"
DEFAULT \'10014\'
+ CONSTRAINT nn NOT NULL
CHECK(VALUE ~ \'^\d{5}$\' OR
VALUE ~ \'^\d{5}-\d{4}$\');
+ COMMENT ON CONSTRAINT nn
+ ON DOMAIN dump_test.us_postal_code IS \'not null\';
COMMENT ON CONSTRAINT us_postal_code_check
ON DOMAIN dump_test.us_postal_code IS \'check it\';',
regexp => qr/^
- \QCREATE DOMAIN dump_test.us_postal_code AS text COLLATE pg_catalog."C" DEFAULT '10014'::text\E\n\s+
+ \QCREATE DOMAIN dump_test.us_postal_code AS text COLLATE pg_catalog."C" CONSTRAINT nn NOT NULL DEFAULT '10014'::text\E\n\s+
\QCONSTRAINT us_postal_code_check CHECK \E
\Q(((VALUE ~ '^\d{5}\E
\$\Q'::text) OR (VALUE ~ '^\d{5}-\d{4}\E\$
\Q'::text)));\E(.|\n)*
- \QCOMMENT ON CONSTRAINT us_postal_code_check ON DOMAIN dump_test.us_postal_code IS 'check it';\E
/xm,
like =>
{ %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
@@ -2061,6 +2063,30 @@ my %tests = (
},
},
+ 'COMMENT ON CONSTRAINT ON DOMAIN (1)' => {
+ regexp => qr/^
+ \QCOMMENT ON CONSTRAINT nn ON DOMAIN dump_test.us_postal_code IS 'not null';\E
+ /xm,
+ like =>
+ { %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_measurement => 1,
+ },
+ },
+
+ 'COMMENT ON CONSTRAINT ON DOMAIN (2)' => {
+ regexp => qr/^
+ \QCOMMENT ON CONSTRAINT us_postal_code_check ON DOMAIN dump_test.us_postal_code IS 'check it';\E
+ /xm,
+ like =>
+ { %full_runs, %dump_test_schema_runs, section_pre_data => 1, },
+ unlike => {
+ exclude_dump_test_schema => 1,
+ only_dump_measurement => 1,
+ },
+ },
+
'CREATE FUNCTION dump_test.pltestlang_call_handler' => {
create_order => 17,
create_sql => 'CREATE FUNCTION dump_test.pltestlang_call_handler()
--
2.39.5
On Fri, Jul 18, 2025 at 5:11 AM Álvaro Herrera <alvherre@kurilemu.de> wrote:
Anyway, here's a patch.
one minor issue in getDomainConstraints:
for (int i = 0, j = 0; i < ntups; i++)
{
char contype = (PQgetvalue(res, i, i_contype))[0];
....
constraint->contype = *(PQgetvalue(res, i, i_contype));
}
for the same code simplicity,
``constraint->contype = *(PQgetvalue(res, i, i_contype));``
can change to
`` constraint->contype = contype; ``
?
other than that, looks good to me.
On 2025-Jul-18, jian he wrote:
one minor issue in getDomainConstraints:
for (int i = 0, j = 0; i < ntups; i++)
{
char contype = (PQgetvalue(res, i, i_contype))[0];
....
constraint->contype = *(PQgetvalue(res, i, i_contype));
}for the same code simplicity,
``constraint->contype = *(PQgetvalue(res, i, i_contype));``
can change to
`` constraint->contype = contype; ``
?
other than that, looks good to me.
Thanks, changed it that way.
I noticed some other issues however. First, you had removed the contype
comparisons in repairDependencyLoop; I put them back several versions of
the patch ago, but I had mistakenly made them reference the wrong array
item -- in all three places. Doh.
Second, the name comparisons to determine whether to list the constraint
name in the "CREATE DOMAIN .. NOT NULL" syntax was wrong: it was using
fmtId() around the constraint name, but of course that would have never
worked, because we're comparing to the original string, not a quoted
name. So if you had a domain called, say
CREATE DOMAIN "my domain" AS int NOT NULL;
then pg_dump would have said
CREATE DOMAIN "my domain" AS int CONSTRAINT "my domain_not_null" NOT NULL;
even though listing the name is unnecessary. This wouldn't have made
the dump fail, so it's borderline; but it would be /slightly/ ugly.
Lastly, I made dumpDomain print unadorned "NOT NULL" if a pg_constraint
row for the constraint is not found and yet we observe typnotnull set.
This can be considered catalog corruption (the row should be there), but
I think this is better than having pg_dump just crash if that row is not
there.
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
Voy a acabar con todos los humanos / con los humanos yo acabaré
voy a acabar con todos (bis) / con todos los humanos acabaré ¡acabaré! (Bender)
On Tue, Jul 15, 2025 at 03:40:38PM +0200, �lvaro Herrera wrote:
On 2025-Jul-15, jian he wrote:
we should let:
dumpConstraint handles dumping separate "NOT VALID" domain constraints along
with their comments.
dumpDomain: handles dumping "inlined" valid (not separate) domain constraints
together with their comments.
This became commit 0858f0f.
@@ -18487,8 +18493,25 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo) .description = "CHECK CONSTRAINT", .section = SECTION_POST_DATA, .createStmt = q->data, .dropStmt = delq->data)); + + if (coninfo->dobj.dump & DUMP_COMPONENT_COMMENT) + { + char *qtypname; + + PQExpBuffer conprefix = createPQExpBuffer(); + qtypname = pg_strdup(fmtId(tyinfo->dobj.name)); + + appendPQExpBuffer(conprefix, "CONSTRAINT %s ON DOMAIN", + fmtId(coninfo->dobj.name)); + + dumpComment(fout, conprefix->data, qtypname, + tyinfo->dobj.namespace->dobj.name, + tyinfo->rolname, + coninfo->dobj.catId, 0, tyinfo->dobj.dumpId);
The last argument gives the dump object on which the comment has a dependency.
Since this is the case of a separately-dumped constraint, the comment needs to
depend on that constraint (coninfo), not on the domain (tyinfo):
- coninfo->dobj.catId, 0, tyinfo->dobj.dumpId);
+ coninfo->dobj.catId, 0, coninfo->dobj.dumpId);
I didn't encounter a failure from this, but sufficient restore parallelism
might reach a failure. A failure would look like a "does not exist" error in
the COMMENT command, due to the constraint not yet existing.
dumpTableConstraintComment() is an older case that optimally handles the last
dumpComment() arg.
In the absence of objections, I'll make it so.
On 2025-Sep-12, Noah Misch wrote:
The last argument gives the dump object on which the comment has a dependency.
Since this is the case of a separately-dumped constraint, the comment needs to
depend on that constraint (coninfo), not on the domain (tyinfo):- coninfo->dobj.catId, 0, tyinfo->dobj.dumpId); + coninfo->dobj.catId, 0, coninfo->dobj.dumpId);I didn't encounter a failure from this, but sufficient restore parallelism
might reach a failure. A failure would look like a "does not exist" error in
the COMMENT command, due to the constraint not yet existing.
dumpTableConstraintComment() is an older case that optimally handles the last
dumpComment() arg.
Sounds sane.
In the absence of objections, I'll make it so.
Please do, thanks.
--
Álvaro Herrera PostgreSQL Developer — https://www.EnterpriseDB.com/