diff options
author | Peter Eisentraut | 2025-03-28 12:53:37 +0000 |
---|---|---|
committer | Peter Eisentraut | 2025-03-28 12:53:37 +0000 |
commit | cdc168ad4b22ea4183f966688b245cabb5935d1f (patch) | |
tree | 1755b8898eadbb54ceaee15acb612952f9bcaeb7 /src/backend/commands | |
parent | 747ddd38cbf6d32bca496e69c1efb2ae4fe333cc (diff) |
Add support for not-null constraints on virtual generated columns
This was left out of the original patch for virtual generated columns
(commit 83ea6c54025).
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 <[email protected]>
Reviewed-by: Xuneng Zhou <[email protected]>
Reviewed-by: Navneet Kumar <[email protected]>
Reviewed-by: Álvaro Herrera <[email protected]>
Discussion: https://postgr.es/m/CACJufxHArQysbDkWFmvK+D1TPHQWWTxWN15cMuUaTYX3xhQXgg@mail.gmail.com
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/indexcmds.c | 10 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 71 |
2 files changed, 66 insertions, 15 deletions
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 89cc83e8843..33c2106c17c 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -1118,10 +1118,12 @@ DefineIndex(Oid tableId, if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL) ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - stmt->isconstraint ? - errmsg("unique constraints on virtual generated columns are not supported") : - errmsg("indexes on virtual generated columns are not supported"))); + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + stmt->primary ? + errmsg("primary keys on virtual generated columns are not supported") : + stmt->isconstraint ? + errmsg("unique constraints on virtual generated columns are not supported") : + errmsg("indexes on virtual generated columns are not supported")); } /* diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index afb25007613..10624353b0a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -6101,6 +6101,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) TupleDesc newTupDesc; bool needscan = false; List *notnull_attrs; + List *notnull_virtual_attrs; int i; ListCell *l; EState *estate; @@ -6185,22 +6186,32 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL); } - notnull_attrs = NIL; + notnull_attrs = notnull_virtual_attrs = NIL; if (newrel || tab->verify_new_notnull) { /* * If we are rebuilding the tuples OR if we added any new but not * verified not-null constraints, check all not-null constraints. This * is a bit of overkill but it minimizes risk of bugs. + * + * notnull_attrs does *not* collect attribute numbers for not-null + * constraints over virtual generated columns; instead, they are + * collected in notnull_virtual_attrs. */ for (i = 0; i < newTupDesc->natts; i++) { Form_pg_attribute attr = TupleDescAttr(newTupDesc, i); if (attr->attnotnull && !attr->attisdropped) - notnull_attrs = lappend_int(notnull_attrs, attr->attnum); + { + if (attr->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL) + notnull_attrs = lappend_int(notnull_attrs, attr->attnum); + else + notnull_virtual_attrs = lappend_int(notnull_virtual_attrs, + attr->attnum); + } } - if (notnull_attrs) + if (notnull_attrs || notnull_virtual_attrs) needscan = true; } @@ -6214,6 +6225,29 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) List *dropped_attrs = NIL; ListCell *lc; Snapshot snapshot; + ResultRelInfo *rInfo = NULL; + + /* + * When adding or changing a virtual generated column with a not-null + * constraint, we need to evaluate whether the generation expression + * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we + * prepare a dummy ResultRelInfo. + */ + if (notnull_virtual_attrs != NIL) + { + MemoryContext oldcontext; + + Assert(newTupDesc->constr->has_generated_virtual); + Assert(newTupDesc->constr->has_not_null); + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + rInfo = makeNode(ResultRelInfo); + InitResultRelInfo(rInfo, + oldrel, + 0, /* dummy rangetable index */ + NULL, + estate->es_instrument); + MemoryContextSwitchTo(oldcontext); + } if (newrel) ereport(DEBUG1, @@ -6394,6 +6428,26 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap) } } + if (notnull_virtual_attrs != NIL) + { + AttrNumber attnum; + + attnum = ExecRelGenVirtualNotNull(rInfo, insertslot, + estate, + notnull_virtual_attrs); + if (attnum != InvalidAttrNumber) + { + Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1); + + ereport(ERROR, + errcode(ERRCODE_NOT_NULL_VIOLATION), + errmsg("column \"%s\" of relation \"%s\" contains null values", + NameStr(attr->attname), + RelationGetRelationName(oldrel)), + errtablecol(oldrel, attnum)); + } + } + foreach(l, tab->constraints) { NewConstraint *con = lfirst(l); @@ -7843,14 +7897,6 @@ ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, errmsg("cannot alter system column \"%s\"", colName))); - /* TODO: see transformColumnDefinition() */ - if (TupleDescAttr(RelationGetDescr(rel), attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("not-null constraints are not supported on virtual generated columns"), - errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.", - colName, RelationGetRelationName(rel)))); - /* See if there's already a constraint */ tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum); if (HeapTupleIsValid(tuple)) @@ -8519,6 +8565,9 @@ ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.", colName, RelationGetRelationName(rel)))); + if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull) + tab->verify_new_notnull = true; + /* * We need to prevent this because a change of expression could affect a * row filter and inject expressions that are not permitted in a row |