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

Commit d361ed9

Browse files
author
Commitfest Bot
committed
[CF 5554] Support NOT VALID / VALIDATE constraint options for named NOT NULL constraints
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5554 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CAGPqQf2jXkkcHzicPJD6_PjMCp1cVG-ZNtftp9cvWn6XUve9wA@mail.gmail.com Author(s): Rushabh Lathia
2 parents 7afca7e + da3fe65 commit d361ed9

File tree

29 files changed

+1101
-106
lines changed

29 files changed

+1101
-106
lines changed

contrib/postgres_fdw/postgres_fdw.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5549,7 +5549,15 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
55495549
"SELECT relname, "
55505550
" attname, "
55515551
" format_type(atttypid, atttypmod), "
5552-
" attnotnull, "
5552+
" attnotnull, ");
5553+
5554+
/* NOT VALID NOT NULL columns are supported since Postgres 18 */
5555+
if (PQserverVersion(conn) >= 180000)
5556+
appendStringInfoString(&buf, "attnotnullvalid, ");
5557+
else
5558+
appendStringInfoString(&buf, "attnotnull AS attnotnullvalid, ");
5559+
5560+
appendStringInfoString(&buf,
55535561
" pg_get_expr(adbin, adrelid), ");
55545562

55555563
/* Generated columns are supported since Postgres 12 */
@@ -5651,6 +5659,7 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
56515659
char *attname;
56525660
char *typename;
56535661
char *attnotnull;
5662+
char *attnotnullvalid;
56545663
char *attgenerated;
56555664
char *attdefault;
56565665
char *collname;
@@ -5663,14 +5672,15 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
56635672
attname = PQgetvalue(res, i, 1);
56645673
typename = PQgetvalue(res, i, 2);
56655674
attnotnull = PQgetvalue(res, i, 3);
5666-
attdefault = PQgetisnull(res, i, 4) ? NULL :
5667-
PQgetvalue(res, i, 4);
5668-
attgenerated = PQgetisnull(res, i, 5) ? NULL :
5675+
attnotnullvalid = PQgetvalue(res, i, 4);
5676+
attdefault = PQgetisnull(res, i, 5) ? NULL :
56695677
PQgetvalue(res, i, 5);
5670-
collname = PQgetisnull(res, i, 6) ? NULL :
5678+
attgenerated = PQgetisnull(res, i, 6) ? NULL :
56715679
PQgetvalue(res, i, 6);
5672-
collnamespace = PQgetisnull(res, i, 7) ? NULL :
5680+
collname = PQgetisnull(res, i, 7) ? NULL :
56735681
PQgetvalue(res, i, 7);
5682+
collnamespace = PQgetisnull(res, i, 8) ? NULL :
5683+
PQgetvalue(res, i, 8);
56745684

56755685
if (first_item)
56765686
first_item = false;
@@ -5714,7 +5724,11 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
57145724

57155725
/* Add NOT NULL if needed */
57165726
if (import_not_null && attnotnull[0] == 't')
5727+
{
57175728
appendStringInfoString(&buf, " NOT NULL");
5729+
if (attnotnullvalid[0] == 'f')
5730+
appendStringInfoString(&buf, " NOT VALID");
5731+
}
57185732
}
57195733
while (++i < numrows &&
57205734
strcmp(PQgetvalue(res, i, 0), tablename) == 0);

doc/src/sgml/catalogs.sgml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1260,7 +1260,16 @@
12601260
<structfield>attnotnull</structfield> <type>bool</type>
12611261
</para>
12621262
<para>
1263-
This column has a not-null constraint.
1263+
This column has a (possibly unvalidated) not-null constraint.
1264+
</para></entry>
1265+
</row>
1266+
1267+
<row>
1268+
<entry role="catalog_table_entry"><para role="column_definition">
1269+
<structfield>attnotnullvalid</structfield> <type>bool</type>
1270+
</para>
1271+
<para>
1272+
Whether the not-null constraint, if one exists, has been validated.
12641273
</para></entry>
12651274
</row>
12661275

doc/src/sgml/ref/alter_table.sgml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
243243
entire table; however, if a valid <literal>CHECK</literal> constraint is
244244
found which proves no <literal>NULL</literal> can exist, then the
245245
table scan is skipped.
246+
If a column has an invalid not-null constraint,
247+
<literal>SET NOT NULL</literal> validates it.
246248
</para>
247249

248250
<para>
@@ -458,8 +460,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
458460
<para>
459461
This form adds a new constraint to a table using the same constraint
460462
syntax as <link linkend="sql-createtable"><command>CREATE TABLE</command></link>, plus the option <literal>NOT
461-
VALID</literal>, which is currently only allowed for foreign key
462-
and CHECK constraints.
463+
VALID</literal>, which is currently only allowed for foreign key,
464+
<literal>CHECK</literal> constraints and not-null constraints.
463465
</para>
464466

465467
<para>
@@ -586,7 +588,7 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
586588
<term><literal>VALIDATE CONSTRAINT</literal></term>
587589
<listitem>
588590
<para>
589-
This form validates a foreign key or check constraint that was
591+
This form validates a foreign key, check, or not-null constraint that was
590592
previously created as <literal>NOT VALID</literal>, by scanning the
591593
table to ensure there are no rows for which the constraint is not
592594
satisfied. If the constraint is not enforced, an error is thrown.

src/backend/access/common/tupdesc.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ populate_compact_attribute_internal(Form_pg_attribute src,
7474
dst->atthasmissing = src->atthasmissing;
7575
dst->attisdropped = src->attisdropped;
7676
dst->attgenerated = (src->attgenerated != '\0');
77-
dst->attnotnull = src->attnotnull;
77+
dst->attnullability = !src->attnotnull ? ATTNULLABLE_NONE :
78+
src->attnotnullvalid ? ATTNULLABLE_VALID : ATTNULLABLE_INVALID;
7879

7980
switch (src->attalign)
8081
{
@@ -252,6 +253,7 @@ CreateTupleDescCopy(TupleDesc tupdesc)
252253
Form_pg_attribute att = TupleDescAttr(desc, i);
253254

254255
att->attnotnull = false;
256+
att->attnotnullvalid = false;
255257
att->atthasdef = false;
256258
att->atthasmissing = false;
257259
att->attidentity = '\0';
@@ -298,6 +300,7 @@ CreateTupleDescTruncatedCopy(TupleDesc tupdesc, int natts)
298300
Form_pg_attribute att = TupleDescAttr(desc, i);
299301

300302
att->attnotnull = false;
303+
att->attnotnullvalid = false;
301304
att->atthasdef = false;
302305
att->atthasmissing = false;
303306
att->attidentity = '\0';
@@ -418,6 +421,7 @@ TupleDescCopy(TupleDesc dst, TupleDesc src)
418421
Form_pg_attribute att = TupleDescAttr(dst, i);
419422

420423
att->attnotnull = false;
424+
att->attnotnullvalid = false;
421425
att->atthasdef = false;
422426
att->atthasmissing = false;
423427
att->attidentity = '\0';
@@ -464,6 +468,7 @@ TupleDescCopyEntry(TupleDesc dst, AttrNumber dstAttno,
464468

465469
/* since we're not copying constraints or defaults, clear these */
466470
dstAtt->attnotnull = false;
471+
dstAtt->attnotnullvalid = false;
467472
dstAtt->atthasdef = false;
468473
dstAtt->atthasmissing = false;
469474
dstAtt->attidentity = '\0';
@@ -613,6 +618,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
613618
return false;
614619
if (attr1->attnotnull != attr2->attnotnull)
615620
return false;
621+
if (attr1->attnotnullvalid != attr2->attnotnullvalid)
622+
return false;
616623
if (attr1->atthasdef != attr2->atthasdef)
617624
return false;
618625
if (attr1->attidentity != attr2->attidentity)
@@ -841,6 +848,7 @@ TupleDescInitEntry(TupleDesc desc,
841848
att->attndims = attdim;
842849

843850
att->attnotnull = false;
851+
att->attnotnullvalid = false;
844852
att->atthasdef = false;
845853
att->atthasmissing = false;
846854
att->attidentity = '\0';
@@ -904,6 +912,7 @@ TupleDescInitBuiltinEntry(TupleDesc desc,
904912
att->attndims = attdim;
905913

906914
att->attnotnull = false;
915+
att->attnotnullvalid = false;
907916
att->atthasdef = false;
908917
att->atthasmissing = false;
909918
att->attidentity = '\0';

src/backend/bootstrap/bootstrap.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,9 @@ DefineAttr(char *name, char *type, int attnum, int nullness)
615615
attrtypes[attnum]->attnotnull = true;
616616
}
617617
}
618+
619+
/* Not-null constraints on system catalogs are always valid. */
620+
attrtypes[attnum]->attnotnullvalid = attrtypes[attnum]->attnotnull;
618621
}
619622

620623

src/backend/catalog/genbki.pl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,9 @@ sub morph_row_for_pgattr
986986
$row->{attnotnull} = 'f';
987987
}
988988

989+
# Not-null constraints on system catalogs are always valid.
990+
$row->{attnotnullvalid} = $row->{attnotnull};
991+
989992
Catalog::AddDefaultValues($row, $pgattr_schema, 'pg_attribute');
990993
return;
991994
}

src/backend/catalog/heap.c

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ static const FormData_pg_attribute a1 = {
151151
.attalign = TYPALIGN_SHORT,
152152
.attstorage = TYPSTORAGE_PLAIN,
153153
.attnotnull = true,
154+
.attnotnullvalid = true,
154155
.attislocal = true,
155156
};
156157

@@ -164,6 +165,7 @@ static const FormData_pg_attribute a2 = {
164165
.attalign = TYPALIGN_INT,
165166
.attstorage = TYPSTORAGE_PLAIN,
166167
.attnotnull = true,
168+
.attnotnullvalid = true,
167169
.attislocal = true,
168170
};
169171

@@ -177,6 +179,7 @@ static const FormData_pg_attribute a3 = {
177179
.attalign = TYPALIGN_INT,
178180
.attstorage = TYPSTORAGE_PLAIN,
179181
.attnotnull = true,
182+
.attnotnullvalid = true,
180183
.attislocal = true,
181184
};
182185

@@ -190,6 +193,7 @@ static const FormData_pg_attribute a4 = {
190193
.attalign = TYPALIGN_INT,
191194
.attstorage = TYPSTORAGE_PLAIN,
192195
.attnotnull = true,
196+
.attnotnullvalid = true,
193197
.attislocal = true,
194198
};
195199

@@ -203,6 +207,7 @@ static const FormData_pg_attribute a5 = {
203207
.attalign = TYPALIGN_INT,
204208
.attstorage = TYPSTORAGE_PLAIN,
205209
.attnotnull = true,
210+
.attnotnullvalid = true,
206211
.attislocal = true,
207212
};
208213

@@ -222,6 +227,7 @@ static const FormData_pg_attribute a6 = {
222227
.attalign = TYPALIGN_INT,
223228
.attstorage = TYPSTORAGE_PLAIN,
224229
.attnotnull = true,
230+
.attnotnullvalid = true,
225231
.attislocal = true,
226232
};
227233

@@ -753,6 +759,7 @@ InsertPgAttributeTuples(Relation pg_attribute_rel,
753759
slot[slotCount]->tts_values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(attrs->attstorage);
754760
slot[slotCount]->tts_values[Anum_pg_attribute_attcompression - 1] = CharGetDatum(attrs->attcompression);
755761
slot[slotCount]->tts_values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(attrs->attnotnull);
762+
slot[slotCount]->tts_values[Anum_pg_attribute_attnotnullvalid - 1] = BoolGetDatum(attrs->attnotnullvalid);
756763
slot[slotCount]->tts_values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(attrs->atthasdef);
757764
slot[slotCount]->tts_values[Anum_pg_attribute_atthasmissing - 1] = BoolGetDatum(attrs->atthasmissing);
758765
slot[slotCount]->tts_values[Anum_pg_attribute_attidentity - 1] = CharGetDatum(attrs->attidentity);
@@ -1714,6 +1721,7 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
17141721

17151722
/* Remove any not-null constraint the column may have */
17161723
attStruct->attnotnull = false;
1724+
attStruct->attnotnullvalid = false;
17171725

17181726
/* Unset this so no one tries to look up the generation expression */
17191727
attStruct->attgenerated = '\0';
@@ -2616,12 +2624,17 @@ AddRelationNewConstraints(Relation rel,
26162624
errmsg("cannot add not-null constraint on system column \"%s\"",
26172625
strVal(linitial(cdef->keys))));
26182626

2627+
Assert(cdef->initially_valid != cdef->skip_validation);
2628+
26192629
/*
26202630
* If the column already has a not-null constraint, we don't want
2621-
* to add another one; just adjust inheritance status as needed.
2631+
* to add another one; adjust inheritance status as needed. This
2632+
* also checks whether the existing constraint matches the
2633+
* requested validity.
26222634
*/
2623-
if (AdjustNotNullInheritance(RelationGetRelid(rel), colnum,
2624-
is_local, cdef->is_no_inherit))
2635+
if (AdjustNotNullInheritance(rel, colnum, is_local,
2636+
cdef->is_no_inherit,
2637+
cdef->skip_validation))
26252638
continue;
26262639

26272640
/*
@@ -2826,11 +2839,24 @@ MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr,
28262839
{
28272840
if (is_local)
28282841
con->conislocal = true;
2829-
else if (pg_add_s16_overflow(con->coninhcount, 1,
2842+
else
2843+
{
2844+
if(pg_add_s16_overflow(con->coninhcount, 1,
28302845
&con->coninhcount))
2831-
ereport(ERROR,
2832-
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2833-
errmsg("too many inheritance parents"));
2846+
ereport(ERROR,
2847+
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2848+
errmsg("too many inheritance parents"));
2849+
2850+
/*
2851+
* If the child already has a valid constraint and we are
2852+
* creating an invalid one with same definition on it. The
2853+
* child's constraint will remain valid, but can no longer be
2854+
* marked as local.
2855+
*/
2856+
if (!is_initially_valid && con->convalidated &&
2857+
is_enforced && con->conenforced)
2858+
con->conislocal = false;
2859+
}
28342860
}
28352861

28362862
if (is_no_inherit)

0 commit comments

Comments
 (0)