diff options
author | Peter Eisentraut <peter@eisentraut.org> | 2025-03-28 13:53:37 +0100 |
---|---|---|
committer | Peter Eisentraut <peter@eisentraut.org> | 2025-03-28 13:53:37 +0100 |
commit | cdc168ad4b22ea4183f966688b245cabb5935d1f (patch) | |
tree | 1755b8898eadbb54ceaee15acb612952f9bcaeb7 /src/backend/commands/tablecmds.c | |
parent | 747ddd38cbf6d32bca496e69c1efb2ae4fe333cc (diff) | |
download | postgresql-cdc168ad4b22ea4183f966688b245cabb5935d1f.tar.gz postgresql-cdc168ad4b22ea4183f966688b245cabb5935d1f.zip |
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 <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
Diffstat (limited to 'src/backend/commands/tablecmds.c')
-rw-r--r-- | src/backend/commands/tablecmds.c | 71 |
1 files changed, 60 insertions, 11 deletions
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 |