diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/executor/execMain.c | 92 |
1 files changed, 91 insertions, 1 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 2a5d39fcea6..643714a948c 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.256.2.6 2006/04/26 23:01:58 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.256.2.7 2008/08/08 17:01:34 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,9 +45,11 @@ #include "miscadmin.h" #include "optimizer/clauses.h" #include "optimizer/var.h" +#include "parser/parse_expr.h" #include "parser/parsetree.h" #include "storage/smgr.h" #include "utils/acl.h" +#include "utils/builtins.h" #include "utils/guc.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -76,6 +78,7 @@ static void initResultRelInfo(ResultRelInfo *resultRelInfo, List *rangeTable, CmdType operation, bool doInstrument); +static void ExecCheckPlanOutput(Relation resultRel, List *targetList); static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, long numberTuples, @@ -631,6 +634,9 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly) * heap_insert will be scribbling on the source relation!). UPDATE and * DELETE always need a filter, since there's always a junk 'ctid' * attribute present --- no need to look first. + * + * This section of code is also a convenient place to verify that the + * output of an INSERT or UPDATE matches the target table(s). */ { bool junk_filter_needed = false; @@ -690,6 +696,10 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly) PlanState *subplan = appendplans[i]; JunkFilter *j; + if (operation == CMD_UPDATE) + ExecCheckPlanOutput(resultRelInfo->ri_RelationDesc, + subplan->plan->targetlist); + j = ExecInitJunkFilter(subplan->plan->targetlist, resultRelInfo->ri_RelationDesc->rd_att->tdhasoid, ExecAllocTableSlot(estate->es_tupleTable)); @@ -709,6 +719,10 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly) /* Normal case with just one JunkFilter */ JunkFilter *j; + if (operation == CMD_INSERT || operation == CMD_UPDATE) + ExecCheckPlanOutput(estate->es_result_relation_info->ri_RelationDesc, + planstate->plan->targetlist); + j = ExecInitJunkFilter(planstate->plan->targetlist, tupType->tdhasoid, ExecAllocTableSlot(estate->es_tupleTable)); @@ -722,7 +736,13 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly) } } else + { + if (operation == CMD_INSERT) + ExecCheckPlanOutput(estate->es_result_relation_info->ri_RelationDesc, + planstate->plan->targetlist); + estate->es_junkFilter = NULL; + } } /* @@ -958,6 +978,76 @@ ExecContextForcesOids(PlanState *planstate, bool *hasoids) return false; } +/* + * Verify that the tuples to be produced by INSERT or UPDATE match the + * target relation's rowtype + * + * We do this to guard against stale plans. If plan invalidation is + * functioning properly then we should never get a failure here, but better + * safe than sorry. Note that this is called after we have obtained lock + * on the target rel, so the rowtype can't change underneath us. + * + * The plan output is represented by its targetlist, because that makes + * handling the dropped-column case easier. + */ +static void +ExecCheckPlanOutput(Relation resultRel, List *targetList) +{ + TupleDesc resultDesc = RelationGetDescr(resultRel); + int attno = 0; + ListCell *lc; + + foreach(lc, targetList) + { + TargetEntry *tle = (TargetEntry *) lfirst(lc); + Form_pg_attribute attr; + + if (tle->resjunk) + continue; /* ignore junk tlist items */ + + if (attno >= resultDesc->natts) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("table row type and query-specified row type do not match"), + errdetail("Query has too many columns."))); + attr = resultDesc->attrs[attno++]; + + if (!attr->attisdropped) + { + /* Normal case: demand type match */ + if (exprType((Node *) tle->expr) != attr->atttypid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("table row type and query-specified row type do not match"), + errdetail("Table has type %s at ordinal position %d, but query expects %s.", + format_type_be(attr->atttypid), + attno, + format_type_be(exprType((Node *) tle->expr))))); + } + else + { + /* + * For a dropped column, we can't check atttypid (it's likely 0). + * In any case the planner has most likely inserted an INT4 null. + * What we insist on is just *some* NULL constant. + */ + if (!IsA(tle->expr, Const) || + !((Const *) tle->expr)->constisnull) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("table row type and query-specified row type do not match"), + errdetail("Query provides a value for a dropped column at ordinal position %d.", + attno))); + } + } + if (attno != resultDesc->natts) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("table row type and query-specified row type do not match"), + errdetail("Query has too few columns."))); +} + + /* ---------------------------------------------------------------- * ExecEndPlan * |