diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 86f005b104..702727bf7f 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.250.2.2 2009/12/29 17:41:09 heikki Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.250.2.3 2010/01/11 15:31:12 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -599,17 +599,23 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, /* * We really only care about number of attributes and data type. * Also, we can ignore type mismatch on columns that are dropped - * in the destination type, so long as the physical storage - * matches. This is helpful in some cases involving out-of-date - * cached plans. Also, we have to allow the case that the slot - * has more columns than the Var's type, because we might be - * looking at the output of a subplan that includes resjunk - * columns. (XXX it would be nice to verify that the extra - * columns are all marked resjunk, but we haven't got access to - * the subplan targetlist here...) Resjunk columns should always - * be at the end of a targetlist, so it's sufficient to ignore - * them here; but we need to use ExecEvalWholeRowSlow to get rid - * of them in the eventual output tuples. + * in the destination type, so long as (1) the physical storage + * matches or (2) the actual column value is NULL. Case (1) is + * helpful in some cases involving out-of-date cached plans, while + * case (2) is expected behavior in situations such as an INSERT + * into a table with dropped columns (the planner typically + * generates an INT4 NULL regardless of the dropped column type). + * If we find a dropped column and cannot verify that case (1) + * holds, we have to use ExecEvalWholeRowSlow to check (2) for + * each row. Also, we have to allow the case that the slot has + * more columns than the Var's type, because we might be looking + * at the output of a subplan that includes resjunk columns. + * (XXX it would be nice to verify that the extra columns are all + * marked resjunk, but we haven't got access to the subplan + * targetlist here...) Resjunk columns should always be at the end + * of a targetlist, so it's sufficient to ignore them here; but we + * need to use ExecEvalWholeRowSlow to get rid of them in the + * eventual output tuples. */ var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); @@ -623,7 +629,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, slot_tupdesc->natts, var_tupdesc->natts))); else if (var_tupdesc->natts < slot_tupdesc->natts) - needslow = true; + needslow = true; /* need to trim trailing atts */ for (i = 0; i < var_tupdesc->natts; i++) { @@ -643,11 +649,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, if (vattr->attlen != sattr->attlen || vattr->attalign != sattr->attalign) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("table row type and query-specified row type do not match"), - errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", - i + 1))); + needslow = true; /* need runtime check for null */ } ReleaseTupleDesc(var_tupdesc); @@ -757,7 +759,7 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext, /* ---------------------------------------------------------------- * ExecEvalWholeRowSlow * - * Returns a Datum for a whole-row variable, in the "slow" case where + * Returns a Datum for a whole-row variable, in the "slow" cases where * we can't just copy the subplan's output. * ---------------------------------------------------------------- */ @@ -770,24 +772,45 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, HeapTuple tuple; TupleDesc var_tupdesc; HeapTupleHeader dtuple; + int i; if (isDone) *isDone = ExprSingleResult; *isNull = false; /* - * Currently, the only case handled here is stripping of trailing resjunk - * fields, which we do in a slightly chintzy way by just adjusting the - * tuple's natts header field. Possibly there will someday be a need for - * more-extensive rearrangements, in which case it'd be worth - * disassembling and reassembling the tuple (perhaps use a JunkFilter for - * that?) + * Currently, the only data modification case handled here is stripping of + * trailing resjunk fields, which we do in a slightly chintzy way by just + * adjusting the tuple's natts header field. Possibly there will someday + * be a need for more-extensive rearrangements, in which case we'd + * probably use tupconvert.c. */ Assert(variable->vartype != RECORDOID); var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); tuple = ExecFetchSlotTuple(slot); + Assert(HeapTupleHeaderGetNatts(tuple->t_data) >= var_tupdesc->natts); + + /* Check to see if any dropped attributes are non-null */ + for (i = 0; i < var_tupdesc->natts; i++) + { + Form_pg_attribute vattr = var_tupdesc->attrs[i]; + Form_pg_attribute sattr = slot->tts_tupleDescriptor->attrs[i]; + + if (!vattr->attisdropped) + continue; /* already checked non-dropped cols */ + if (heap_attisnull(tuple, i+1)) + continue; /* null is always okay */ + if (vattr->attlen != sattr->attlen || + vattr->attalign != sattr->attalign) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("table row type and query-specified row type do not match"), + errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.", + i + 1))); + } + /* * We have to make a copy of the tuple so we can safely insert the Datum * overhead fields, which are not set in on-disk tuples; not to mention @@ -800,7 +823,6 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, HeapTupleHeaderSetTypeId(dtuple, variable->vartype); HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); - Assert(HeapTupleHeaderGetNatts(dtuple) >= var_tupdesc->natts); HeapTupleHeaderSetNatts(dtuple, var_tupdesc->natts); ReleaseTupleDesc(var_tupdesc);