mirror of
https://github.com/postgres/postgres.git
synced 2025-06-13 07:41:39 +03:00
Add support for not-null constraints on virtual generated columns
This was left out of the original patch for virtual generated columns
(commit 83ea6c5402
).
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
This commit is contained in:
@ -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
|
||||
|
Reference in New Issue
Block a user