@@ -325,7 +325,8 @@ static void AlterSeqNamespaces(Relation classRel, Relation rel,
325
325
LOCKMODE lockmode);
326
326
static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
327
327
bool recurse, bool recursing, LOCKMODE lockmode);
328
- static ObjectAddress ATExecValidateConstraint(Relation rel, char *constrName,
328
+ static ObjectAddress ATExecValidateConstraint(List **wqueue,
329
+ Relation rel, char *constrName,
329
330
bool recurse, bool recursing, LOCKMODE lockmode);
330
331
static int transformColumnNameList(Oid relId, List *colList,
331
332
int16 *attnums, Oid *atttypids);
@@ -339,7 +340,6 @@ static Oid transformFkeyCheckAttrs(Relation pkrel,
339
340
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
340
341
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
341
342
Oid *funcid);
342
- static void validateCheckConstraint(Relation rel, HeapTuple constrtup);
343
343
static void validateForeignKeyConstraint(char *conname,
344
344
Relation rel, Relation pkrel,
345
345
Oid pkindOid, Oid constraintOid);
@@ -4399,13 +4399,13 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
4399
4399
address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
4400
4400
break;
4401
4401
case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
4402
- address = ATExecValidateConstraint(rel, cmd->name, false , false,
4403
- lockmode);
4402
+ address = ATExecValidateConstraint(wqueue, rel, cmd->name, false,
4403
+ false, lockmode);
4404
4404
break;
4405
4405
case AT_ValidateConstraintRecurse: /* VALIDATE CONSTRAINT with
4406
4406
* recursion */
4407
- address = ATExecValidateConstraint(rel, cmd->name, true, false ,
4408
- lockmode);
4407
+ address = ATExecValidateConstraint(wqueue, rel, cmd->name, true,
4408
+ false, lockmode);
4409
4409
break;
4410
4410
case AT_DropConstraint: /* DROP CONSTRAINT */
4411
4411
ATExecDropConstraint(rel, cmd->name, cmd->behavior,
@@ -9314,8 +9314,8 @@ ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
9314
9314
* was already validated, InvalidObjectAddress is returned.
9315
9315
*/
9316
9316
static ObjectAddress
9317
- ATExecValidateConstraint(Relation rel, char *constrName, bool recurse ,
9318
- bool recursing, LOCKMODE lockmode)
9317
+ ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
9318
+ bool recurse, bool recursing, LOCKMODE lockmode)
9319
9319
{
9320
9320
Relation conrel;
9321
9321
SysScanDesc scan;
@@ -9361,27 +9361,31 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
9361
9361
9362
9362
if (!con->convalidated)
9363
9363
{
9364
+ AlteredTableInfo *tab;
9364
9365
HeapTuple copyTuple;
9365
9366
Form_pg_constraint copy_con;
9366
9367
9367
9368
if (con->contype == CONSTRAINT_FOREIGN)
9368
9369
{
9369
- Relation refrel;
9370
+ NewConstraint *newcon;
9371
+ Constraint *fkconstraint;
9370
9372
9371
- /*
9372
- * Triggers are already in place on both tables, so a concurrent
9373
- * write that alters the result here is not possible. Normally we
9374
- * can run a query here to do the validation, which would only
9375
- * require AccessShareLock. In some cases, it is possible that we
9376
- * might need to fire triggers to perform the check, so we take a
9377
- * lock at RowShareLock level just in case.
9378
- */
9379
- refrel = table_open(con->confrelid, RowShareLock);
9373
+ /* Queue validation for phase 3 */
9374
+ fkconstraint = makeNode(Constraint);
9375
+ /* for now this is all we need */
9376
+ fkconstraint->conname = constrName;
9380
9377
9381
- validateForeignKeyConstraint(constrName, rel, refrel,
9382
- con->conindid,
9383
- con->oid);
9384
- table_close(refrel, NoLock);
9378
+ newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9379
+ newcon->name = constrName;
9380
+ newcon->contype = CONSTR_FOREIGN;
9381
+ newcon->refrelid = con->confrelid;
9382
+ newcon->refindid = con->conindid;
9383
+ newcon->conid = con->oid;
9384
+ newcon->qual = (Node *) fkconstraint;
9385
+
9386
+ /* Find or create work queue entry for this table */
9387
+ tab = ATGetQueueEntry(wqueue, rel);
9388
+ tab->constraints = lappend(tab->constraints, newcon);
9385
9389
9386
9390
/*
9387
9391
* We disallow creating invalid foreign keys to or from
@@ -9392,6 +9396,11 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
9392
9396
{
9393
9397
List *children = NIL;
9394
9398
ListCell *child;
9399
+ NewConstraint *newcon;
9400
+ bool isnull;
9401
+ Datum val;
9402
+ char *conbin;
9403
+
9395
9404
9396
9405
/*
9397
9406
* If we're recursing, the parent has already done this, so skip
@@ -9431,12 +9440,30 @@ ATExecValidateConstraint(Relation rel, char *constrName, bool recurse,
9431
9440
/* find_all_inheritors already got lock */
9432
9441
childrel = table_open(childoid, NoLock);
9433
9442
9434
- ATExecValidateConstraint(childrel, constrName, false,
9443
+ ATExecValidateConstraint(wqueue, childrel, constrName, false,
9435
9444
true, lockmode);
9436
9445
table_close(childrel, NoLock);
9437
9446
}
9438
9447
9439
- validateCheckConstraint(rel, tuple);
9448
+ /* Queue validation for phase 3 */
9449
+ newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9450
+ newcon->name = constrName;
9451
+ newcon->contype = CONSTR_CHECK;
9452
+ newcon->refrelid = InvalidOid;
9453
+ newcon->refindid = InvalidOid;
9454
+ newcon->conid = con->oid;
9455
+
9456
+ val = SysCacheGetAttr(CONSTROID, tuple,
9457
+ Anum_pg_constraint_conbin, &isnull);
9458
+ if (isnull)
9459
+ elog(ERROR, "null conbin for constraint %u", con->oid);
9460
+
9461
+ conbin = TextDatumGetCString(val);
9462
+ newcon->qual = (Node *) stringToNode(conbin);
9463
+
9464
+ /* Find or create work queue entry for this table */
9465
+ tab = ATGetQueueEntry(wqueue, rel);
9466
+ tab->constraints = lappend(tab->constraints, newcon);
9440
9467
9441
9468
/*
9442
9469
* Invalidate relcache so that others see the new validated
@@ -9810,86 +9837,6 @@ checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
9810
9837
}
9811
9838
}
9812
9839
9813
- /*
9814
- * Scan the existing rows in a table to verify they meet a proposed
9815
- * CHECK constraint.
9816
- *
9817
- * The caller must have opened and locked the relation appropriately.
9818
- */
9819
- static void
9820
- validateCheckConstraint(Relation rel, HeapTuple constrtup)
9821
- {
9822
- EState *estate;
9823
- Datum val;
9824
- char *conbin;
9825
- Expr *origexpr;
9826
- ExprState *exprstate;
9827
- TableScanDesc scan;
9828
- ExprContext *econtext;
9829
- MemoryContext oldcxt;
9830
- TupleTableSlot *slot;
9831
- Form_pg_constraint constrForm;
9832
- bool isnull;
9833
- Snapshot snapshot;
9834
-
9835
- /*
9836
- * VALIDATE CONSTRAINT is a no-op for foreign tables and partitioned
9837
- * tables.
9838
- */
9839
- if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE ||
9840
- rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9841
- return;
9842
-
9843
- constrForm = (Form_pg_constraint) GETSTRUCT(constrtup);
9844
-
9845
- estate = CreateExecutorState();
9846
-
9847
- /*
9848
- * XXX this tuple doesn't really come from a syscache, but this doesn't
9849
- * matter to SysCacheGetAttr, because it only wants to be able to fetch
9850
- * the tupdesc
9851
- */
9852
- val = SysCacheGetAttr(CONSTROID, constrtup, Anum_pg_constraint_conbin,
9853
- &isnull);
9854
- if (isnull)
9855
- elog(ERROR, "null conbin for constraint %u",
9856
- constrForm->oid);
9857
- conbin = TextDatumGetCString(val);
9858
- origexpr = (Expr *) stringToNode(conbin);
9859
- exprstate = ExecPrepareExpr(origexpr, estate);
9860
-
9861
- econtext = GetPerTupleExprContext(estate);
9862
- slot = table_slot_create(rel, NULL);
9863
- econtext->ecxt_scantuple = slot;
9864
-
9865
- snapshot = RegisterSnapshot(GetLatestSnapshot());
9866
- scan = table_beginscan(rel, snapshot, 0, NULL);
9867
-
9868
- /*
9869
- * Switch to per-tuple memory context and reset it for each tuple
9870
- * produced, so we don't leak memory.
9871
- */
9872
- oldcxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
9873
-
9874
- while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
9875
- {
9876
- if (!ExecCheck(exprstate, econtext))
9877
- ereport(ERROR,
9878
- (errcode(ERRCODE_CHECK_VIOLATION),
9879
- errmsg("check constraint \"%s\" is violated by some row",
9880
- NameStr(constrForm->conname)),
9881
- errtableconstraint(rel, NameStr(constrForm->conname))));
9882
-
9883
- ResetExprContext(econtext);
9884
- }
9885
-
9886
- MemoryContextSwitchTo(oldcxt);
9887
- table_endscan(scan);
9888
- UnregisterSnapshot(snapshot);
9889
- ExecDropSingleTupleTableSlot(slot);
9890
- FreeExecutorState(estate);
9891
- }
9892
-
9893
9840
/*
9894
9841
* Scan the existing rows in a table to verify they meet a proposed FK
9895
9842
* constraint.
0 commit comments