1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-27 23:21:58 +03:00

Clean up inefficiency in ExecRelCheck, and cause it to do the right

thing when there are multiple result relations.  Formerly, during
something like 'UPDATE foo*', foo's constraints and *only* foo's
constraints would be applied to all foo's children.  Wrong-o ...
This commit is contained in:
Tom Lane
2000-08-06 04:26:40 +00:00
parent 925418d2fa
commit 8ae23135bc
4 changed files with 80 additions and 139 deletions

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.119 2000/07/14 22:17:42 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.120 2000/08/06 04:26:40 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -827,9 +827,10 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
* Check the constraints of the tuple * Check the constraints of the tuple
* ---------------- * ----------------
*/ */
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
if (rel->rd_att->constr) if (rel->rd_att->constr)
ExecConstraints("CopyFrom", rel, tuple, estate); ExecConstraints("CopyFrom", rel, slot, estate);
/* ---------------- /* ----------------
* OK, store the tuple and create index entries for it * OK, store the tuple and create index entries for it
@ -838,10 +839,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
heap_insert(rel, tuple); heap_insert(rel, tuple);
if (relationInfo->ri_NumIndices > 0) if (relationInfo->ri_NumIndices > 0)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
}
/* AFTER ROW INSERT Triggers */ /* AFTER ROW INSERT Triggers */
if (rel->trigdesc && if (rel->trigdesc &&

View File

@ -27,7 +27,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.122 2000/07/12 02:37:00 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.123 2000/08/06 04:26:26 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -1296,10 +1296,6 @@ ExecAppend(TupleTableSlot *slot,
resultRelationInfo = estate->es_result_relation_info; resultRelationInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc; resultRelationDesc = resultRelationInfo->ri_RelationDesc;
/*
* have to add code to preform unique checking here. cim -12/1/89
*/
/* BEFORE ROW INSERT Triggers */ /* BEFORE ROW INSERT Triggers */
if (resultRelationDesc->trigdesc && if (resultRelationDesc->trigdesc &&
resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0)
@ -1320,18 +1316,20 @@ ExecAppend(TupleTableSlot *slot,
} }
/* /*
* Check the constraints of a tuple * Check the constraints of the tuple
*/ */
if (resultRelationDesc->rd_att->constr) if (resultRelationDesc->rd_att->constr)
ExecConstraints("ExecAppend", resultRelationDesc, tuple, estate); ExecConstraints("ExecAppend", resultRelationDesc, slot, estate);
/* /*
* insert the tuple * insert the tuple
*/ */
newId = heap_insert(resultRelationDesc, /* relation desc */ newId = heap_insert(resultRelationDesc, tuple);
tuple); /* heap tuple */
IncrAppended(); IncrAppended();
(estate->es_processed)++;
estate->es_lastoid = newId;
/* /*
* process indices * process indices
@ -1343,8 +1341,6 @@ ExecAppend(TupleTableSlot *slot,
numIndices = resultRelationInfo->ri_NumIndices; numIndices = resultRelationInfo->ri_NumIndices;
if (numIndices > 0) if (numIndices > 0)
ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false);
(estate->es_processed)++;
estate->es_lastoid = newId;
/* AFTER ROW INSERT Triggers */ /* AFTER ROW INSERT Triggers */
if (resultRelationDesc->trigdesc) if (resultRelationDesc->trigdesc)
@ -1466,7 +1462,7 @@ ExecReplace(TupleTableSlot *slot,
*/ */
if (IsBootstrapProcessingMode()) if (IsBootstrapProcessingMode())
{ {
elog(DEBUG, "ExecReplace: replace can't run without transactions"); elog(NOTICE, "ExecReplace: replace can't run without transactions");
return; return;
} }
@ -1481,12 +1477,6 @@ ExecReplace(TupleTableSlot *slot,
resultRelationInfo = estate->es_result_relation_info; resultRelationInfo = estate->es_result_relation_info;
resultRelationDesc = resultRelationInfo->ri_RelationDesc; resultRelationDesc = resultRelationInfo->ri_RelationDesc;
/*
* have to add code to preform unique checking here. in the event of
* unique tuples, this becomes a deletion of the original tuple
* affected by the replace. cim -12/1/89
*/
/* BEFORE ROW UPDATE Triggers */ /* BEFORE ROW UPDATE Triggers */
if (resultRelationDesc->trigdesc && if (resultRelationDesc->trigdesc &&
resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0)
@ -1507,11 +1497,11 @@ ExecReplace(TupleTableSlot *slot,
} }
/* /*
* Check the constraints of a tuple * Check the constraints of the tuple
*/ */
if (resultRelationDesc->rd_att->constr) if (resultRelationDesc->rd_att->constr)
ExecConstraints("ExecReplace", resultRelationDesc, tuple, estate); ExecConstraints("ExecReplace", resultRelationDesc, slot, estate);
/* /*
* replace the heap tuple * replace the heap tuple
@ -1555,9 +1545,9 @@ lreplace:;
/* /*
* Note: instead of having to update the old index tuples associated * Note: instead of having to update the old index tuples associated
* with the heap tuple, all we do is form and insert new index * with the heap tuple, all we do is form and insert new index
* tuples.. This is because replaces are actually deletes and inserts * tuples. This is because replaces are actually deletes and inserts
* and index tuple deletion is done automagically by the vaccuum * and index tuple deletion is done automagically by the vacuum
* deamon.. All we do is insert new index tuples. -cim 9/27/89 * daemon. All we do is insert new index tuples. -cim 9/27/89
*/ */
/* /*
@ -1579,109 +1569,55 @@ lreplace:;
ExecARUpdateTriggers(estate, tupleid, tuple); ExecARUpdateTriggers(estate, tupleid, tuple);
} }
#ifdef NOT_USED
static HeapTuple
ExecAttrDefault(Relation rel, HeapTuple tuple)
{
int ndef = rel->rd_att->constr->num_defval;
AttrDefault *attrdef = rel->rd_att->constr->defval;
ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext);
HeapTuple newtuple;
Node *expr;
bool isnull;
bool isdone;
Datum val;
Datum *replValue = NULL;
char *replNull = NULL;
char *repl = NULL;
int i;
for (i = 0; i < ndef; i++)
{
if (!heap_attisnull(tuple, attrdef[i].adnum))
continue;
expr = (Node *) stringToNode(attrdef[i].adbin);
val = ExecEvalExprSwitchContext(expr, econtext, &isnull, &isdone);
if (isnull)
continue;
if (repl == NULL)
{
repl = (char *) palloc(rel->rd_att->natts * sizeof(char));
replNull = (char *) palloc(rel->rd_att->natts * sizeof(char));
replValue = (Datum *) palloc(rel->rd_att->natts * sizeof(Datum));
MemSet(repl, ' ', rel->rd_att->natts * sizeof(char));
}
repl[attrdef[i].adnum - 1] = 'r';
replNull[attrdef[i].adnum - 1] = ' ';
replValue[attrdef[i].adnum - 1] = val;
}
if (repl == NULL)
{
/* no changes needed */
newtuple = tuple;
}
else
{
newtuple = heap_modifytuple(tuple, rel, replValue, replNull, repl);
pfree(repl);
pfree(replNull);
pfree(replValue);
heap_freetuple(tuple);
}
FreeMemoryContext(econtext);
return newtuple;
}
#endif
static char * static char *
ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate) ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
{ {
int ncheck = rel->rd_att->constr->num_check; int ncheck = rel->rd_att->constr->num_check;
ConstrCheck *check = rel->rd_att->constr->check; ConstrCheck *check = rel->rd_att->constr->check;
TupleTableSlot *slot = makeNode(TupleTableSlot); MemoryContext oldContext;
RangeTblEntry *rte = makeNode(RangeTblEntry); ExprContext *econtext;
ExprContext *econtext = MakeExprContext(slot,
TransactionCommandContext);
List *rtlist;
List *qual; List *qual;
int i; int i;
slot->val = tuple; /*
slot->ttc_shouldFree = false; * Make sure econtext, expressions, etc are placed in appropriate context.
slot->ttc_descIsNew = true; */
slot->ttc_tupleDescriptor = rel->rd_att; oldContext = MemoryContextSwitchTo(TransactionCommandContext);
slot->ttc_buffer = InvalidBuffer;
slot->ttc_whichplan = -1;
rte->relname = RelationGetRelationName(rel);
rte->ref = makeNode(Attr);
rte->ref->relname = rte->relname;
rte->relid = RelationGetRelid(rel);
/* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */
rtlist = lcons(rte, NIL);
econtext->ecxt_range_table = rtlist; /* phony range table */
/* /*
* Save the de-stringized constraint expressions in command-level * Create or reset the exprcontext for evaluating constraint expressions.
* memory context. XXX should build the above stuff there too,
* instead of doing it over for each tuple.
* XXX Is it sufficient to have just one es_result_relation_constraints
* in an inherited insert/update?
*/ */
if (estate->es_result_relation_constraints == NULL) econtext = estate->es_constraint_exprcontext;
{ if (econtext == NULL)
MemoryContext oldContext; estate->es_constraint_exprcontext = econtext =
MakeExprContext(NULL, TransactionCommandContext);
else
ResetExprContext(econtext);
oldContext = MemoryContextSwitchTo(TransactionCommandContext); /*
* If first time through for current result relation, set up econtext's
* range table to refer to result rel, and build expression nodetrees
* for rel's constraint expressions. All this stuff is kept in
* TransactionCommandContext so it will still be here next time through.
*
* NOTE: if there are multiple result relations (eg, due to inheritance)
* then we leak storage for prior rel's expressions and rangetable.
* This should not be a big problem as long as result rels are processed
* sequentially within a command.
*/
if (econtext->ecxt_range_table == NIL ||
getrelid(1, econtext->ecxt_range_table) != RelationGetRelid(rel))
{
RangeTblEntry *rte = makeNode(RangeTblEntry);
rte->relname = RelationGetRelationName(rel);
rte->ref = makeNode(Attr);
rte->ref->relname = rte->relname;
rte->relid = RelationGetRelid(rel);
/* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */
/* Set up single-entry range table */
econtext->ecxt_range_table = lcons(rte, NIL);
estate->es_result_relation_constraints = estate->es_result_relation_constraints =
(List **) palloc(ncheck * sizeof(List *)); (List **) palloc(ncheck * sizeof(List *));
@ -1691,10 +1627,15 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
qual = (List *) stringToNode(check[i].ccbin); qual = (List *) stringToNode(check[i].ccbin);
estate->es_result_relation_constraints[i] = qual; estate->es_result_relation_constraints[i] = qual;
} }
MemoryContextSwitchTo(oldContext);
} }
/* Done with building long-lived items */
MemoryContextSwitchTo(oldContext);
/* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot;
/* And evaluate the constraints */
for (i = 0; i < ncheck; i++) for (i = 0; i < ncheck; i++)
{ {
qual = estate->es_result_relation_constraints[i]; qual = estate->es_result_relation_constraints[i];
@ -1708,25 +1649,25 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
return check[i].ccname; return check[i].ccname;
} }
pfree(slot); /* NULL result means no error */
pfree(rte);
pfree(rtlist);
FreeExprContext(econtext);
return (char *) NULL; return (char *) NULL;
} }
void void
ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate) ExecConstraints(char *caller, Relation rel,
TupleTableSlot *slot, EState *estate)
{ {
Assert(rel->rd_att->constr); HeapTuple tuple = slot->val;
TupleConstr *constr = rel->rd_att->constr;
if (rel->rd_att->constr->has_not_null) Assert(constr);
if (constr->has_not_null)
{ {
int natts = rel->rd_att->natts;
int attrChk; int attrChk;
for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++) for (attrChk = 1; attrChk <= natts; attrChk++)
{ {
if (rel->rd_att->attrs[attrChk-1]->attnotnull && if (rel->rd_att->attrs[attrChk-1]->attnotnull &&
heap_attisnull(tuple, attrChk)) heap_attisnull(tuple, attrChk))
@ -1735,11 +1676,11 @@ ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
} }
} }
if (rel->rd_att->constr->num_check > 0) if (constr->num_check > 0)
{ {
char *failed; char *failed;
if ((failed = ExecRelCheck(rel, tuple, estate)) != NULL) if ((failed = ExecRelCheck(rel, slot, estate)) != NULL)
elog(ERROR, "%s: rejected due to CHECK constraint %s", elog(ERROR, "%s: rejected due to CHECK constraint %s",
caller, failed); caller, failed);
} }

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: executor.h,v 1.46 2000/07/12 02:37:30 tgl Exp $ * $Id: executor.h,v 1.47 2000/08/06 04:26:27 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -56,8 +56,8 @@ extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate, extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate,
int feature, Node *limoffset, Node *limcount); int feature, Node *limoffset, Node *limcount);
extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate); extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
extern void ExecConstraints(char *caller, Relation rel, HeapTuple tuple, extern void ExecConstraints(char *caller, Relation rel,
EState *estate); TupleTableSlot *slot, EState *estate);
extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
ItemPointer tid); ItemPointer tid);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: execnodes.h,v 1.44 2000/07/14 22:17:58 tgl Exp $ * $Id: execnodes.h,v 1.45 2000/08/06 04:26:29 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -215,7 +215,6 @@ typedef struct EState
Snapshot es_snapshot; Snapshot es_snapshot;
List *es_range_table; List *es_range_table;
RelationInfo *es_result_relation_info; RelationInfo *es_result_relation_info;
List **es_result_relation_constraints;
Relation es_into_relation_descriptor; Relation es_into_relation_descriptor;
ParamListInfo es_param_list_info; ParamListInfo es_param_list_info;
ParamExecData *es_param_exec_vals; /* this is for subselects */ ParamExecData *es_param_exec_vals; /* this is for subselects */
@ -224,6 +223,9 @@ typedef struct EState
uint32 es_processed; /* # of tuples processed */ uint32 es_processed; /* # of tuples processed */
Oid es_lastoid; /* last oid processed (by INSERT) */ Oid es_lastoid; /* last oid processed (by INSERT) */
List *es_rowMark; /* not good place, but there is no other */ List *es_rowMark; /* not good place, but there is no other */
/* these two fields are storage space for ExecConstraints(): */
List **es_result_relation_constraints;
ExprContext *es_constraint_exprcontext;
/* Below is to re-evaluate plan qual in READ COMMITTED mode */ /* Below is to re-evaluate plan qual in READ COMMITTED mode */
struct Plan *es_origPlan; struct Plan *es_origPlan;
Pointer es_evalPlanQual; Pointer es_evalPlanQual;