Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit bdb5cd4

Browse files
jianhe-funCommitfest Bot
authored and
Commitfest Bot
committed
ALTER DOMAIN ADD NOT NULL NOT VALID
we have NOT NULL NO VALID for table constraints, we can make domain have NOT VALID not-null constraint too. ALTER DOMAIN SET NOT NULL works similarly to its ALTER TABLE counterpart. It validates an existing invalid NOT NULL constraint and updates pg_constraint.convalidated to true. ALTER DOMAIN ADD NOT NULL will add a new, valid not-null constraint. If the domain already has an NOT VALID not-null constraint, the command will result in an error. If domain already have VALID not-null, add a NOT VALID is no-op. \dD changes: for domain's not null not valid constraint: now column "Nullable" will display as "not null not valid". domain valid not-null constraint works as before. discussed: https://postgr.es/m/CACJufxGcABLgmH951SJkkihK+FW8KR3=odBhXEVCF9atQbur2Q@mail.gmail.com
1 parent e050af2 commit bdb5cd4

File tree

10 files changed

+219
-27
lines changed

10 files changed

+219
-27
lines changed

doc/src/sgml/catalogs.sgml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9515,6 +9515,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
95159515
<para>
95169516
<structfield>typnotnull</structfield> represents a not-null
95179517
constraint on a type. Used for domains only.
9518+
Note: the not-null constraint on domain may not validated.
95189519
</para></entry>
95199520
</row>
95209521

doc/src/sgml/ref/alter_domain.sgml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,6 @@ ALTER DOMAIN <replaceable class="parameter">name</replaceable>
9292
valid using <command>ALTER DOMAIN ... VALIDATE CONSTRAINT</command>.
9393
Newly inserted or updated rows are always checked against all
9494
constraints, even those marked <literal>NOT VALID</literal>.
95-
<literal>NOT VALID</literal> is only accepted for <literal>CHECK</literal> constraints.
9695
</para>
9796
</listitem>
9897
</varlistentry>

src/backend/catalog/pg_constraint.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -651,8 +651,8 @@ findNotNullConstraint(Oid relid, const char *colname)
651651
}
652652

653653
/*
654-
* Find and return the pg_constraint tuple that implements a validated
655-
* not-null constraint for the given domain.
654+
* Find and return the pg_constraint tuple that implements not-null constraint
655+
* for the given domain. it may be NOT VALID.
656656
*/
657657
HeapTuple
658658
findDomainNotNullConstraint(Oid typid)
@@ -675,13 +675,9 @@ findDomainNotNullConstraint(Oid typid)
675675
{
676676
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(conTup);
677677

678-
/*
679-
* We're looking for a NOTNULL constraint that's marked validated.
680-
*/
678+
/* We're looking for a NOTNULL constraint */
681679
if (con->contype != CONSTRAINT_NOTNULL)
682680
continue;
683-
if (!con->convalidated)
684-
continue;
685681

686682
/* Found it */
687683
retval = heap_copytuple(conTup);

src/backend/commands/typecmds.c

Lines changed: 108 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2757,12 +2757,70 @@ AlterDomainNotNull(List *names, bool notNull)
27572757
checkDomainOwner(tup);
27582758

27592759
/* Is the domain already set to the desired constraint? */
2760-
if (typTup->typnotnull == notNull)
2760+
if (!typTup->typnotnull && !notNull)
27612761
{
27622762
table_close(typrel, RowExclusiveLock);
27632763
return address;
27642764
}
27652765

2766+
if (typTup->typnotnull && notNull)
2767+
{
2768+
ScanKeyData key[1];
2769+
SysScanDesc scan;
2770+
Relation pg_constraint;
2771+
Form_pg_constraint copy_con;
2772+
HeapTuple conTup;
2773+
HeapTuple copyTuple;
2774+
2775+
pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
2776+
2777+
/* Look for NOT NULL Constraints on this domain */
2778+
ScanKeyInit(&key[0],
2779+
Anum_pg_constraint_contypid,
2780+
BTEqualStrategyNumber, F_OIDEQ,
2781+
ObjectIdGetDatum(domainoid));
2782+
2783+
scan = systable_beginscan(pg_constraint, ConstraintTypidIndexId, true,
2784+
NULL, 1, key);
2785+
2786+
while (HeapTupleIsValid(conTup = systable_getnext(scan)))
2787+
{
2788+
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(conTup);
2789+
2790+
if (con->contype != CONSTRAINT_NOTNULL)
2791+
continue;
2792+
2793+
/*
2794+
* ALTER DOMAIN SET NOT NULL will validate the NOT VALID not-null
2795+
* constraint, also set the pg_constraint.convalidated to true.
2796+
*/
2797+
if (!con->convalidated)
2798+
{
2799+
validateDomainNotNullConstraint(domainoid);
2800+
copyTuple = heap_copytuple(conTup);
2801+
2802+
copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
2803+
copy_con->convalidated = true;
2804+
CatalogTupleUpdate(pg_constraint, &copyTuple->t_self, copyTuple);
2805+
2806+
InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
2807+
2808+
heap_freetuple(copyTuple);
2809+
}
2810+
break;
2811+
}
2812+
systable_endscan(scan);
2813+
table_close(pg_constraint, AccessShareLock);
2814+
2815+
table_close(typrel, RowExclusiveLock);
2816+
2817+
/*
2818+
* If we already validated the existing NOT VALID not-null constraint
2819+
* then we don't need install another not-null constraint, exit now.
2820+
*/
2821+
return address;
2822+
}
2823+
27662824
if (notNull)
27672825
{
27682826
Constraint *constr;
@@ -2990,7 +3048,45 @@ AlterDomainAddConstraint(List *names, Node *newConstraint,
29903048
}
29913049
else if (constr->contype == CONSTR_NOTNULL)
29923050
{
2993-
/* Is the domain already set NOT NULL? */
3051+
HeapTuple conTup;
3052+
ScanKeyData key[1];
3053+
SysScanDesc scan;
3054+
Relation pg_constraint;
3055+
3056+
pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
3057+
3058+
/* Look for NOT NULL Constraints on this domain */
3059+
ScanKeyInit(&key[0],
3060+
Anum_pg_constraint_contypid,
3061+
BTEqualStrategyNumber, F_OIDEQ,
3062+
ObjectIdGetDatum(domainoid));
3063+
3064+
scan = systable_beginscan(pg_constraint, ConstraintTypidIndexId, true,
3065+
NULL, 1, key);
3066+
3067+
while (HeapTupleIsValid(conTup = systable_getnext(scan)))
3068+
{
3069+
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(conTup);
3070+
3071+
if (con->contype != CONSTRAINT_NOTNULL)
3072+
continue;
3073+
3074+
/*
3075+
* can not add not-null constraint if the domain already have NOT
3076+
* VALID not-null constraint
3077+
*/
3078+
if (!constr->skip_validation && !con->convalidated)
3079+
ereport(ERROR,
3080+
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
3081+
errmsg("incompatible NOT VALID constraint \"%s\" on domain \"%s\"",
3082+
NameStr(con->conname), NameStr(typTup->typname)),
3083+
errhint("You might need to validate it using %s.",
3084+
"ALTER DOMAIN ... VALIDATE CONSTRAINT"));
3085+
break;
3086+
}
3087+
systable_endscan(scan);
3088+
table_close(pg_constraint, AccessShareLock);
3089+
29943090
if (typTup->typnotnull)
29953091
{
29963092
table_close(typrel, RowExclusiveLock);
@@ -3081,16 +3177,20 @@ AlterDomainValidateConstraint(List *names, const char *constrName)
30813177
constrName, TypeNameToString(typename))));
30823178

30833179
con = (Form_pg_constraint) GETSTRUCT(tuple);
3084-
if (con->contype != CONSTRAINT_CHECK)
3180+
if (con->contype != CONSTRAINT_CHECK && con->contype != CONSTRAINT_NOTNULL)
30853181
ereport(ERROR,
30863182
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
3087-
errmsg("constraint \"%s\" of domain \"%s\" is not a check constraint",
3183+
errmsg("constraint \"%s\" of domain \"%s\" is not a check or not-null constraint",
30883184
constrName, TypeNameToString(typename))));
30893185

3090-
val = SysCacheGetAttrNotNull(CONSTROID, tuple, Anum_pg_constraint_conbin);
3091-
conbin = TextDatumGetCString(val);
3092-
3093-
validateDomainCheckConstraint(domainoid, conbin);
3186+
if (con->contype == CONSTRAINT_CHECK)
3187+
{
3188+
val = SysCacheGetAttrNotNull(CONSTROID, tuple, Anum_pg_constraint_conbin);
3189+
conbin = TextDatumGetCString(val);
3190+
validateDomainCheckConstraint(domainoid, conbin);
3191+
}
3192+
else
3193+
validateDomainNotNullConstraint(domainoid);
30943194

30953195
/*
30963196
* Now update the catalog, while we have the door open.

src/backend/parser/gram.y

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4391,11 +4391,13 @@ DomainConstraintElem:
43914391
n->contype = CONSTR_NOTNULL;
43924392
n->location = @1;
43934393
n->keys = list_make1(makeString("value"));
4394-
/* no NOT VALID, NO INHERIT support */
4394+
/* NO INHERIT is not supported */
43954395
processCASbits($3, @3, "NOT NULL",
43964396
NULL, NULL, NULL,
4397-
NULL, NULL, yyscanner);
4398-
n->initially_valid = true;
4397+
&n->skip_validation,
4398+
NULL, yyscanner);
4399+
n->is_enforced = true;
4400+
n->initially_valid = !n->skip_validation;
43994401
$$ = (Node *) n;
44004402
}
44014403
;

src/bin/pg_dump/pg_dump.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8250,7 +8250,8 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
82508250
int i_tableoid,
82518251
i_oid,
82528252
i_conname,
8253-
i_consrc;
8253+
i_consrc,
8254+
i_contype;
82548255
int ntups;
82558256

82568257
if (!fout->is_prepared[PREPQUERY_GETDOMAINCONSTRAINTS])
@@ -8260,9 +8261,10 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
82608261
"PREPARE getDomainConstraints(pg_catalog.oid) AS\n"
82618262
"SELECT tableoid, oid, conname, "
82628263
"pg_catalog.pg_get_constraintdef(oid) AS consrc, "
8263-
"convalidated "
8264+
"convalidated, "
8265+
"contype "
82648266
"FROM pg_catalog.pg_constraint "
8265-
"WHERE contypid = $1 AND contype = 'c' "
8267+
"WHERE contypid = $1 "
82668268
"ORDER BY conname");
82678269

82688270
ExecuteSqlStatement(fout, query->data);
@@ -8282,6 +8284,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
82828284
i_oid = PQfnumber(res, "oid");
82838285
i_conname = PQfnumber(res, "conname");
82848286
i_consrc = PQfnumber(res, "consrc");
8287+
i_contype = PQfnumber(res, "contype");
82858288

82868289
constrinfo = (ConstraintInfo *) pg_malloc(ntups * sizeof(ConstraintInfo));
82878290

@@ -8300,7 +8303,7 @@ getDomainConstraints(Archive *fout, TypeInfo *tyinfo)
83008303
constrinfo[i].dobj.namespace = tyinfo->dobj.namespace;
83018304
constrinfo[i].contable = NULL;
83028305
constrinfo[i].condomain = tyinfo;
8303-
constrinfo[i].contype = 'c';
8306+
constrinfo[i].contype = *(PQgetvalue(res, i, i_contype));
83048307
constrinfo[i].condef = pg_strdup(PQgetvalue(res, i, i_consrc));
83058308
constrinfo[i].confrelid = InvalidOid;
83068309
constrinfo[i].conindex = 0;
@@ -12497,8 +12500,18 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
1249712500
appendPQExpBuffer(q, " COLLATE %s", fmtQualifiedDumpable(coll));
1249812501
}
1249912502

12500-
if (typnotnull[0] == 't')
12503+
if (typnotnull[0] == 't' && fout->remoteVersion < 190000)
1250112504
appendPQExpBufferStr(q, " NOT NULL");
12505+
else
12506+
{
12507+
for (i = 0; i < tyinfo->nDomChecks; i++)
12508+
{
12509+
ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
12510+
12511+
if (!domcheck->separate && domcheck->contype == 'n')
12512+
appendPQExpBufferStr(q, " NOT NULL");
12513+
}
12514+
}
1250212515

1250312516
if (typdefault != NULL)
1250412517
{
@@ -12518,7 +12531,7 @@ dumpDomain(Archive *fout, const TypeInfo *tyinfo)
1251812531
{
1251912532
ConstraintInfo *domcheck = &(tyinfo->domChecks[i]);
1252012533

12521-
if (!domcheck->separate)
12534+
if (!domcheck->separate && domcheck->contype == 'c')
1252212535
appendPQExpBuffer(q, "\n\tCONSTRAINT %s %s",
1252312536
fmtId(domcheck->dobj.name), domcheck->condef);
1252412537
}
@@ -18388,7 +18401,7 @@ dumpConstraint(Archive *fout, const ConstraintInfo *coninfo)
1838818401
.dropStmt = delq->data));
1838918402
}
1839018403
}
18391-
else if (coninfo->contype == 'c' && tbinfo == NULL)
18404+
else if (tbinfo == NULL)
1839218405
{
1839318406
/* CHECK constraint on a domain */
1839418407
TypeInfo *tyinfo = coninfo->condomain;

src/bin/pg_dump/t/002_pg_dump.pl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,22 @@
12051205
},
12061206
},
12071207

1208+
'DOMAIN CONSTRAINT NOT NULL / NOT VALID' => {
1209+
create_sql => 'CREATE DOMAIN dump_test.test_domain_nn AS INT;
1210+
ALTER DOMAIN dump_test.test_domain_nn ADD CONSTRAINT nn NOT NULL NOT VALID;',
1211+
regexp => qr/^
1212+
\QALTER DOMAIN dump_test.test_domain_nn\E \n^\s+
1213+
\QADD CONSTRAINT nn NOT NULL NOT VALID;\E
1214+
/xm,
1215+
like => {
1216+
%full_runs, %dump_test_schema_runs, section_post_data => 1,
1217+
},
1218+
unlike => {
1219+
exclude_dump_test_schema => 1,
1220+
only_dump_measurement => 1,
1221+
},
1222+
},
1223+
12081224
'CONSTRAINT NOT NULL / NOT VALID (child1)' => {
12091225
regexp => qr/^
12101226
\QCREATE TABLE dump_test.test_table_nn_chld1 (\E\n

src/bin/psql/describe.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4558,7 +4558,9 @@ listDomains(const char *pattern, bool verbose, bool showSystem)
45584558
" pg_catalog.format_type(t.typbasetype, t.typtypmod) as \"%s\",\n"
45594559
" (SELECT c.collname FROM pg_catalog.pg_collation c, pg_catalog.pg_type bt\n"
45604560
" WHERE c.oid = t.typcollation AND bt.oid = t.typbasetype AND t.typcollation <> bt.typcollation) as \"%s\",\n"
4561-
" CASE WHEN t.typnotnull THEN 'not null' END as \"%s\",\n"
4561+
" CASE WHEN t.typnotnull THEN "
4562+
" (SELECT lower(pg_catalog.pg_get_constraintdef(r.oid, true)) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid AND r.contype = " CppAsString2(CONSTRAINT_NOTNULL) ")"
4563+
" END as \"%s\",\n"
45624564
" t.typdefault as \"%s\",\n"
45634565
" pg_catalog.array_to_string(ARRAY(\n"
45644566
" SELECT pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE t.oid = r.contypid AND r.contype = " CppAsString2(CONSTRAINT_CHECK) " ORDER BY r.conname\n"

src/test/regress/expected/domain.out

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,6 +926,47 @@ ALTER DOMAIN things VALIDATE CONSTRAINT meow;
926926
ERROR: column "stuff" of table "thethings" contains values that violate the new constraint
927927
UPDATE thethings SET stuff = 10;
928928
ALTER DOMAIN things VALIDATE CONSTRAINT meow;
929+
SELECT * FROM thethings ORDER BY 1;
930+
stuff
931+
-------
932+
10
933+
(1 row)
934+
935+
ALTER DOMAIN things ADD CONSTRAINT nn1 NOT NULL;
936+
ALTER DOMAIN things ADD CONSTRAINT domain_nn NOT NULL NOT VALID; --no-op
937+
ALTER DOMAIN things DROP NOT NULL;
938+
INSERT INTO thethings VALUES(NULL);
939+
ALTER DOMAIN things ADD CONSTRAINT domain_nn NOT NULL NOT VALID; --ok
940+
INSERT INTO thethings VALUES(NULL); --error
941+
ERROR: domain things does not allow null values
942+
ALTER DOMAIN things ADD CONSTRAINT nn1 NOT NULL; --error
943+
ERROR: incompatible NOT VALID constraint "domain_nn" on domain "things"
944+
HINT: You might need to validate it using ALTER DOMAIN ... VALIDATE CONSTRAINT.
945+
ALTER DOMAIN things SET NOT NULL; --error
946+
ERROR: column "stuff" of table "thethings" contains null values
947+
ALTER DOMAIN things ADD CONSTRAINT domain_nn1 NOT NULL NOT VALID; --no-op
948+
\dD things
949+
List of domains
950+
Schema | Name | Type | Collation | Nullable | Default | Check
951+
--------+--------+---------+-----------+--------------------+---------+--------------------
952+
public | things | integer | | not null not valid | | CHECK (VALUE < 11)
953+
(1 row)
954+
955+
SELECT conname, pg_get_constraintdef(oid)
956+
FROM pg_constraint
957+
WHERE contypid = 'things'::regtype
958+
ORDER BY conname COLLATE "C";
959+
conname | pg_get_constraintdef
960+
-----------+----------------------
961+
domain_nn | NOT NULL NOT VALID
962+
meow | CHECK ((VALUE < 11))
963+
(2 rows)
964+
965+
ALTER DOMAIN things VALIDATE CONSTRAINT domain_nn; --error
966+
ERROR: column "stuff" of table "thethings" contains null values
967+
UPDATE thethings SET stuff = 10 WHERE stuff IS NULL;
968+
ALTER DOMAIN things SET NOT NULL; --ok
969+
ALTER DOMAIN things DROP NOT NULL; --ok
929970
-- Confirm ALTER DOMAIN with RULES.
930971
create table domtab (col1 integer);
931972
create domain dom as integer;

src/test/regress/sql/domain.sql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,28 @@ ALTER DOMAIN things ADD CONSTRAINT meow CHECK (VALUE < 11) NOT VALID;
536536
ALTER DOMAIN things VALIDATE CONSTRAINT meow;
537537
UPDATE thethings SET stuff = 10;
538538
ALTER DOMAIN things VALIDATE CONSTRAINT meow;
539+
SELECT * FROM thethings ORDER BY 1;
540+
ALTER DOMAIN things ADD CONSTRAINT nn1 NOT NULL;
541+
ALTER DOMAIN things ADD CONSTRAINT domain_nn NOT NULL NOT VALID; --no-op
542+
ALTER DOMAIN things DROP NOT NULL;
543+
544+
INSERT INTO thethings VALUES(NULL);
545+
ALTER DOMAIN things ADD CONSTRAINT domain_nn NOT NULL NOT VALID; --ok
546+
INSERT INTO thethings VALUES(NULL); --error
547+
ALTER DOMAIN things ADD CONSTRAINT nn1 NOT NULL; --error
548+
ALTER DOMAIN things SET NOT NULL; --error
549+
ALTER DOMAIN things ADD CONSTRAINT domain_nn1 NOT NULL NOT VALID; --no-op
550+
551+
\dD things
552+
SELECT conname, pg_get_constraintdef(oid)
553+
FROM pg_constraint
554+
WHERE contypid = 'things'::regtype
555+
ORDER BY conname COLLATE "C";
556+
557+
ALTER DOMAIN things VALIDATE CONSTRAINT domain_nn; --error
558+
UPDATE thethings SET stuff = 10 WHERE stuff IS NULL;
559+
ALTER DOMAIN things SET NOT NULL; --ok
560+
ALTER DOMAIN things DROP NOT NULL; --ok
539561

540562
-- Confirm ALTER DOMAIN with RULES.
541563
create table domtab (col1 integer);

0 commit comments

Comments
 (0)