summaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
authorPeter Eisentraut2025-03-28 12:53:37 +0000
committerPeter Eisentraut2025-03-28 12:53:37 +0000
commitcdc168ad4b22ea4183f966688b245cabb5935d1f (patch)
tree1755b8898eadbb54ceaee15acb612952f9bcaeb7 /src/backend/commands
parent747ddd38cbf6d32bca496e69c1efb2ae4fe333cc (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.c10
-rw-r--r--src/backend/commands/tablecmds.c71
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