Skip to content

Commit cdc168a

Browse files
committed
Add support for not-null constraints on virtual generated columns
This was left out of the original patch for virtual generated columns (commit 83ea6c5). This just involves a bit of extra work in the executor to expand the generation expressions and run a "IS NOT NULL" test against them. There is also a bit of work to make sure that not-null constraints are checked during a table rewrite. Author: jian he <jian.universality@gmail.com> Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com> Reviewed-by: Navneet Kumar <thanit3111@gmail.com> Reviewed-by: Álvaro Herrera <alvherre@alvh.no-ip.org> Discussion: https://postgr.es/m/CACJufxHArQysbDkWFmvK+D1TPHQWWTxWN15cMuUaTYX3xhQXgg@mail.gmail.com
1 parent 747ddd3 commit cdc168a

File tree

9 files changed

+360
-119
lines changed

9 files changed

+360
-119
lines changed

src/backend/catalog/heap.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2615,11 +2615,6 @@ AddRelationNewConstraints(Relation rel,
26152615
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
26162616
errmsg("cannot add not-null constraint on system column \"%s\"",
26172617
strVal(linitial(cdef->keys))));
2618-
/* TODO: see transformColumnDefinition() */
2619-
if (get_attgenerated(RelationGetRelid(rel), colnum) == ATTRIBUTE_GENERATED_VIRTUAL)
2620-
ereport(ERROR,
2621-
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2622-
errmsg("not-null constraints are not supported on virtual generated columns"));
26232618

26242619
/*
26252620
* If the column already has a not-null constraint, we don't want
@@ -2935,11 +2930,6 @@ AddRelationNotNullConstraints(Relation rel, List *constraints,
29352930
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
29362931
errmsg("cannot add not-null constraint on system column \"%s\"",
29372932
strVal(linitial(constr->keys))));
2938-
/* TODO: see transformColumnDefinition() */
2939-
if (get_attgenerated(RelationGetRelid(rel), attnum) == ATTRIBUTE_GENERATED_VIRTUAL)
2940-
ereport(ERROR,
2941-
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2942-
errmsg("not-null constraints are not supported on virtual generated columns"));
29432933

29442934
/*
29452935
* A column can only have one not-null constraint, so discard any

src/backend/commands/indexcmds.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,10 +1118,12 @@ DefineIndex(Oid tableId,
11181118

11191119
if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
11201120
ereport(ERROR,
1121-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1122-
stmt->isconstraint ?
1123-
errmsg("unique constraints on virtual generated columns are not supported") :
1124-
errmsg("indexes on virtual generated columns are not supported")));
1121+
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1122+
stmt->primary ?
1123+
errmsg("primary keys on virtual generated columns are not supported") :
1124+
stmt->isconstraint ?
1125+
errmsg("unique constraints on virtual generated columns are not supported") :
1126+
errmsg("indexes on virtual generated columns are not supported"));
11251127
}
11261128

11271129
/*

src/backend/commands/tablecmds.c

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6101,6 +6101,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
61016101
TupleDesc newTupDesc;
61026102
bool needscan = false;
61036103
List *notnull_attrs;
6104+
List *notnull_virtual_attrs;
61046105
int i;
61056106
ListCell *l;
61066107
EState *estate;
@@ -6185,22 +6186,32 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
61856186
ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
61866187
}
61876188

6188-
notnull_attrs = NIL;
6189+
notnull_attrs = notnull_virtual_attrs = NIL;
61896190
if (newrel || tab->verify_new_notnull)
61906191
{
61916192
/*
61926193
* If we are rebuilding the tuples OR if we added any new but not
61936194
* verified not-null constraints, check all not-null constraints. This
61946195
* is a bit of overkill but it minimizes risk of bugs.
6196+
*
6197+
* notnull_attrs does *not* collect attribute numbers for not-null
6198+
* constraints over virtual generated columns; instead, they are
6199+
* collected in notnull_virtual_attrs.
61956200
*/
61966201
for (i = 0; i < newTupDesc->natts; i++)
61976202
{
61986203
Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
61996204

62006205
if (attr->attnotnull && !attr->attisdropped)
6201-
notnull_attrs = lappend_int(notnull_attrs, attr->attnum);
6206+
{
6207+
if (attr->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6208+
notnull_attrs = lappend_int(notnull_attrs, attr->attnum);
6209+
else
6210+
notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6211+
attr->attnum);
6212+
}
62026213
}
6203-
if (notnull_attrs)
6214+
if (notnull_attrs || notnull_virtual_attrs)
62046215
needscan = true;
62056216
}
62066217

@@ -6214,6 +6225,29 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
62146225
List *dropped_attrs = NIL;
62156226
ListCell *lc;
62166227
Snapshot snapshot;
6228+
ResultRelInfo *rInfo = NULL;
6229+
6230+
/*
6231+
* When adding or changing a virtual generated column with a not-null
6232+
* constraint, we need to evaluate whether the generation expression
6233+
* is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6234+
* prepare a dummy ResultRelInfo.
6235+
*/
6236+
if (notnull_virtual_attrs != NIL)
6237+
{
6238+
MemoryContext oldcontext;
6239+
6240+
Assert(newTupDesc->constr->has_generated_virtual);
6241+
Assert(newTupDesc->constr->has_not_null);
6242+
oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6243+
rInfo = makeNode(ResultRelInfo);
6244+
InitResultRelInfo(rInfo,
6245+
oldrel,
6246+
0, /* dummy rangetable index */
6247+
NULL,
6248+
estate->es_instrument);
6249+
MemoryContextSwitchTo(oldcontext);
6250+
}
62176251

62186252
if (newrel)
62196253
ereport(DEBUG1,
@@ -6394,6 +6428,26 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
63946428
}
63956429
}
63966430

6431+
if (notnull_virtual_attrs != NIL)
6432+
{
6433+
AttrNumber attnum;
6434+
6435+
attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6436+
estate,
6437+
notnull_virtual_attrs);
6438+
if (attnum != InvalidAttrNumber)
6439+
{
6440+
Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6441+
6442+
ereport(ERROR,
6443+
errcode(ERRCODE_NOT_NULL_VIOLATION),
6444+
errmsg("column \"%s\" of relation \"%s\" contains null values",
6445+
NameStr(attr->attname),
6446+
RelationGetRelationName(oldrel)),
6447+
errtablecol(oldrel, attnum));
6448+
}
6449+
}
6450+
63976451
foreach(l, tab->constraints)
63986452
{
63996453
NewConstraint *con = lfirst(l);
@@ -7843,14 +7897,6 @@ ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
78437897
errmsg("cannot alter system column \"%s\"",
78447898
colName)));
78457899

7846-
/* TODO: see transformColumnDefinition() */
7847-
if (TupleDescAttr(RelationGetDescr(rel), attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
7848-
ereport(ERROR,
7849-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7850-
errmsg("not-null constraints are not supported on virtual generated columns"),
7851-
errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
7852-
colName, RelationGetRelationName(rel))));
7853-
78547900
/* See if there's already a constraint */
78557901
tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
78567902
if (HeapTupleIsValid(tuple))
@@ -8519,6 +8565,9 @@ ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
85198565
errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
85208566
colName, RelationGetRelationName(rel))));
85218567

8568+
if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8569+
tab->verify_new_notnull = true;
8570+
85228571
/*
85238572
* We need to prevent this because a change of expression could affect a
85248573
* row filter and inject expressions that are not permitted in a row

0 commit comments

Comments
 (0)