Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 8644ad5

Browse files
author
Commitfest Bot
committed
[CF 5782] v1 - foreign key on virtual generated column
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/5782 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/CACJufxEfzfERVwoUaFODz9AOabDOLkNkfTbajB82dSJVuTQQOw@mail.gmail.com Author(s): Jian He
2 parents 706054b + d9be9c8 commit 8644ad5

File tree

9 files changed

+315
-93
lines changed

9 files changed

+315
-93
lines changed

src/backend/commands/copyfrom.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,8 +1346,9 @@ CopyFrom(CopyFromState cstate)
13461346
/* Compute stored generated columns */
13471347
if (resultRelInfo->ri_RelationDesc->rd_att->constr &&
13481348
resultRelInfo->ri_RelationDesc->rd_att->constr->has_generated_stored)
1349-
ExecComputeStoredGenerated(resultRelInfo, estate, myslot,
1350-
CMD_INSERT);
1349+
ExecComputeGenerated(resultRelInfo, estate, myslot,
1350+
CMD_INSERT,
1351+
false);
13511352

13521353
/*
13531354
* If the target is a plain table, check the constraints of

src/backend/commands/tablecmds.c

Lines changed: 55 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
659659
Oid objid, Relation rel, List *domname,
660660
const char *conname);
661661
static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
662-
static void TryReuseForeignKey(Oid oldId, Constraint *con);
662+
static void TryReuseForeignKey(Oid oldId, Constraint *con, Oid oldRelId);
663663
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
664664
List *options, LOCKMODE lockmode);
665665
static void change_owner_fix_column_acls(Oid relationOid,
@@ -8642,17 +8642,17 @@ ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
86428642
* this renders them pointless.
86438643
*/
86448644
RelationClearMissing(rel);
8645+
}
86458646

8646-
/* make sure we don't conflict with later attribute modifications */
8647-
CommandCounterIncrement();
8647+
/* make sure we don't conflict with later attribute modifications */
8648+
CommandCounterIncrement();
86488649

8649-
/*
8650-
* Find everything that depends on the column (constraints, indexes,
8651-
* etc), and record enough information to let us recreate the objects
8652-
* after rewrite.
8653-
*/
8654-
RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8655-
}
8650+
/*
8651+
* Find everything that depends on the column (constraints, indexes,
8652+
* etc), and record enough information to let us recreate the objects
8653+
* after rewrite.
8654+
*/
8655+
RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
86568656

86578657
/*
86588658
* Drop the dependency records of the GENERATED expression, in particular
@@ -10222,19 +10222,6 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
1022210222
errmsg("invalid %s action for foreign key constraint containing generated column",
1022310223
"ON DELETE")));
1022410224
}
10225-
10226-
/*
10227-
* FKs on virtual columns are not supported. This would require
10228-
* various additional support in ri_triggers.c, including special
10229-
* handling in ri_NullCheck(), ri_KeysEqual(),
10230-
* RI_FKey_fk_upd_check_required() (since all virtual columns appear
10231-
* as NULL there). Also not really practical as long as you can't
10232-
* index virtual columns.
10233-
*/
10234-
if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10235-
ereport(ERROR,
10236-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10237-
errmsg("foreign key constraints on virtual generated columns are not supported")));
1023810225
}
1023910226

1024010227
/*
@@ -15665,7 +15652,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
1566515652
/* rewriting neither side of a FK */
1566615653
if (con->contype == CONSTR_FOREIGN &&
1566715654
!rewrite && tab->rewrite == 0)
15668-
TryReuseForeignKey(oldId, con);
15655+
TryReuseForeignKey(oldId, con, oldRelId);
1566915656
con->reset_default_tblspc = true;
1567015657
cmd->subtype = AT_ReAddConstraint;
1567115658
tab->subcmds[AT_PASS_OLD_CONSTR] =
@@ -15822,16 +15809,23 @@ TryReuseIndex(Oid oldId, IndexStmt *stmt)
1582215809
* Stash the old P-F equality operator into the Constraint node, for possible
1582315810
* use by ATAddForeignKeyConstraint() in determining whether revalidation of
1582415811
* this constraint can be skipped.
15812+
*
15813+
* oldId is the old (previous) foreign key pg_constraint oid.
15814+
* oldRelId: the relation where this foreign key constraint is on.
15815+
* revalidation can not be skipped if any foreign key attribute is virtual
15816+
* generated column.
1582515817
*/
1582615818
static void
15827-
TryReuseForeignKey(Oid oldId, Constraint *con)
15819+
TryReuseForeignKey(Oid oldId, Constraint *con, Oid oldRelId)
1582815820
{
1582915821
HeapTuple tup;
15830-
Datum adatum;
15831-
ArrayType *arr;
15832-
Oid *rawarr;
1583315822
int numkeys;
1583415823
int i;
15824+
Relation rel;
15825+
Oid conpfeqop[INDEX_MAX_KEYS];
15826+
AttrNumber conkey[INDEX_MAX_KEYS];
15827+
AttrNumber confkey[INDEX_MAX_KEYS];
15828+
bool fkey_on_virtual_generated = false;
1583515829

1583615830
Assert(con->contype == CONSTR_FOREIGN);
1583715831
Assert(con->old_conpfeqop == NIL); /* already prepared this node */
@@ -15840,20 +15834,41 @@ TryReuseForeignKey(Oid oldId, Constraint *con)
1584015834
if (!HeapTupleIsValid(tup)) /* should not happen */
1584115835
elog(ERROR, "cache lookup failed for constraint %u", oldId);
1584215836

15843-
adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15844-
Anum_pg_constraint_conpfeqop);
15845-
arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15846-
numkeys = ARR_DIMS(arr)[0];
15847-
/* test follows the one in ri_FetchConstraintInfo() */
15848-
if (ARR_NDIM(arr) != 1 ||
15849-
ARR_HASNULL(arr) ||
15850-
ARR_ELEMTYPE(arr) != OIDOID)
15851-
elog(ERROR, "conpfeqop is not a 1-D Oid array");
15852-
rawarr = (Oid *) ARR_DATA_PTR(arr);
15837+
DeconstructFkConstraintRow(tup,
15838+
&numkeys,
15839+
conkey,
15840+
confkey,
15841+
conpfeqop,
15842+
NULL, /* pp_eq_oprs */
15843+
NULL, /* ff_eq_oprs */
15844+
NULL, /* num_fk_del_set_cols */
15845+
NULL); /* fk_del_set_cols */
15846+
15847+
rel = table_open(oldRelId, NoLock);
15848+
15849+
if (rel->rd_att->constr &&
15850+
rel->rd_att->constr->has_generated_virtual)
15851+
{
15852+
TupleDesc tupleDesc = RelationGetDescr(rel);
15853+
15854+
for (i = 0; i < numkeys; i++)
15855+
{
15856+
Form_pg_attribute attr = TupleDescAttr(tupleDesc, conkey[i] - 1);
15857+
if (attr->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
15858+
{
15859+
fkey_on_virtual_generated = true;
15860+
break;
15861+
}
15862+
}
15863+
}
15864+
table_close(rel, NoLock);
1585315865

1585415866
/* stash a List of the operator Oids in our Constraint node */
15855-
for (i = 0; i < numkeys; i++)
15856-
con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15867+
if (!fkey_on_virtual_generated)
15868+
{
15869+
for (i = 0; i < numkeys; i++)
15870+
con->old_conpfeqop = lappend_oid(con->old_conpfeqop, conpfeqop[i]);
15871+
}
1585715872

1585815873
ReleaseSysCache(tup);
1585915874
}

src/backend/executor/execReplication.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -587,8 +587,9 @@ ExecSimpleRelationInsert(ResultRelInfo *resultRelInfo,
587587
/* Compute stored generated columns */
588588
if (rel->rd_att->constr &&
589589
rel->rd_att->constr->has_generated_stored)
590-
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
591-
CMD_INSERT);
590+
ExecComputeGenerated(resultRelInfo, estate, slot,
591+
CMD_INSERT,
592+
false);
592593

593594
/* Check the constraints of the tuple */
594595
if (rel->rd_att->constr)
@@ -684,8 +685,9 @@ ExecSimpleRelationUpdate(ResultRelInfo *resultRelInfo,
684685
/* Compute stored generated columns */
685686
if (rel->rd_att->constr &&
686687
rel->rd_att->constr->has_generated_stored)
687-
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
688-
CMD_UPDATE);
688+
ExecComputeGenerated(resultRelInfo, estate, slot,
689+
CMD_UPDATE,
690+
false);
689691

690692
/* Check the constraints of the tuple */
691693
if (rel->rd_att->constr)

src/backend/executor/execUtils.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,7 @@ ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate)
14041404
{
14051405
/* Compute the info if we didn't already */
14061406
if (!relinfo->ri_extraUpdatedCols_valid)
1407-
ExecInitGenerated(relinfo, estate, CMD_UPDATE);
1407+
ExecInitGenerated(relinfo, estate, CMD_UPDATE, false);
14081408
return relinfo->ri_extraUpdatedCols;
14091409
}
14101410

src/backend/executor/nodeModifyTable.c

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,8 @@ ExecCheckTIDVisible(EState *estate,
415415
*
416416
* This fills the resultRelInfo's ri_GeneratedExprsI/ri_NumGeneratedNeededI or
417417
* ri_GeneratedExprsU/ri_NumGeneratedNeededU fields, depending on cmdtype.
418-
* This is used only for stored generated columns.
418+
* This is mainly used only for stored generated columns. However if
419+
* compute_virtual is true, we do the same for virtual generated column.
419420
*
420421
* If cmdType == CMD_UPDATE, the ri_extraUpdatedCols field is filled too.
421422
* This is used by both stored and virtual generated columns.
@@ -428,7 +429,8 @@ ExecCheckTIDVisible(EState *estate,
428429
void
429430
ExecInitGenerated(ResultRelInfo *resultRelInfo,
430431
EState *estate,
431-
CmdType cmdtype)
432+
CmdType cmdtype,
433+
bool compute_virtual)
432434
{
433435
Relation rel = resultRelInfo->ri_RelationDesc;
434436
TupleDesc tupdesc = RelationGetDescr(rel);
@@ -472,10 +474,15 @@ ExecInitGenerated(ResultRelInfo *resultRelInfo,
472474
Expr *expr;
473475

474476
/* Fetch the GENERATED AS expression tree */
475-
expr = (Expr *) build_column_default(rel, i + 1);
476-
if (expr == NULL)
477-
elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
478-
i + 1, RelationGetRelationName(rel));
477+
if (attgenerated == ATTRIBUTE_GENERATED_STORED)
478+
{
479+
expr = (Expr *) build_column_default(rel, i + 1);
480+
if (expr == NULL)
481+
elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
482+
i + 1, RelationGetRelationName(rel));
483+
}
484+
else
485+
expr = (Expr *) build_generation_expression(rel, i+1);
479486

480487
/*
481488
* If it's an update with a known set of update target columns,
@@ -497,6 +504,11 @@ ExecInitGenerated(ResultRelInfo *resultRelInfo,
497504
ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
498505
ri_NumGeneratedNeeded++;
499506
}
507+
else if (compute_virtual)
508+
{
509+
ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
510+
ri_NumGeneratedNeeded++;
511+
}
500512

501513
/* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
502514
if (cmdtype == CMD_UPDATE)
@@ -537,12 +549,14 @@ ExecInitGenerated(ResultRelInfo *resultRelInfo,
537549
}
538550

539551
/*
540-
* Compute stored generated columns for a tuple
552+
* Compute generated columns for a tuple.
553+
* If compute_virtual is true, we exclusively compute virtual generated columns.
554+
* We do not compute stored and virtual generated columns simultaneously.
541555
*/
542556
void
543-
ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
544-
EState *estate, TupleTableSlot *slot,
545-
CmdType cmdtype)
557+
ExecComputeGenerated(ResultRelInfo *resultRelInfo, EState *estate,
558+
TupleTableSlot *slot, CmdType cmdtype,
559+
bool compute_virtual)
546560
{
547561
Relation rel = resultRelInfo->ri_RelationDesc;
548562
TupleDesc tupdesc = RelationGetDescr(rel);
@@ -554,7 +568,9 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
554568
bool *nulls;
555569

556570
/* We should not be called unless this is true */
557-
Assert(tupdesc->constr && tupdesc->constr->has_generated_stored);
571+
Assert(tupdesc->constr);
572+
Assert(tupdesc->constr->has_generated_stored ||
573+
tupdesc->constr->has_generated_virtual);
558574

559575
/*
560576
* Initialize the expressions if we didn't already, and check whether we
@@ -563,15 +579,15 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
563579
if (cmdtype == CMD_UPDATE)
564580
{
565581
if (resultRelInfo->ri_GeneratedExprsU == NULL)
566-
ExecInitGenerated(resultRelInfo, estate, cmdtype);
582+
ExecInitGenerated(resultRelInfo, estate, cmdtype, compute_virtual);
567583
if (resultRelInfo->ri_NumGeneratedNeededU == 0)
568584
return;
569585
ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
570586
}
571587
else
572588
{
573589
if (resultRelInfo->ri_GeneratedExprsI == NULL)
574-
ExecInitGenerated(resultRelInfo, estate, cmdtype);
590+
ExecInitGenerated(resultRelInfo, estate, cmdtype, compute_virtual);
575591
/* Early exit is impossible given the prior Assert */
576592
Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
577593
ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
@@ -594,7 +610,7 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
594610
Datum val;
595611
bool isnull;
596612

597-
Assert(TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED);
613+
Assert(TupleDescAttr(tupdesc, i)->attgenerated != '\0');
598614

599615
econtext->ecxt_scantuple = slot;
600616

@@ -931,8 +947,9 @@ ExecInsert(ModifyTableContext *context,
931947
*/
932948
if (resultRelationDesc->rd_att->constr &&
933949
resultRelationDesc->rd_att->constr->has_generated_stored)
934-
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
935-
CMD_INSERT);
950+
ExecComputeGenerated(resultRelInfo, estate, slot,
951+
CMD_INSERT,
952+
false);
936953

937954
/*
938955
* If the FDW supports batching, and batching is requested, accumulate
@@ -1058,8 +1075,9 @@ ExecInsert(ModifyTableContext *context,
10581075
*/
10591076
if (resultRelationDesc->rd_att->constr &&
10601077
resultRelationDesc->rd_att->constr->has_generated_stored)
1061-
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
1062-
CMD_INSERT);
1078+
ExecComputeGenerated(resultRelInfo, estate, slot,
1079+
CMD_INSERT,
1080+
false);
10631081

10641082
/*
10651083
* Check any RLS WITH CHECK policies.
@@ -2146,8 +2164,9 @@ ExecUpdatePrepareSlot(ResultRelInfo *resultRelInfo,
21462164
*/
21472165
if (resultRelationDesc->rd_att->constr &&
21482166
resultRelationDesc->rd_att->constr->has_generated_stored)
2149-
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
2150-
CMD_UPDATE);
2167+
ExecComputeGenerated(resultRelInfo, estate, slot,
2168+
CMD_UPDATE,
2169+
false);
21512170
}
21522171

21532172
/*

0 commit comments

Comments
 (0)