Commit 292176a1 authored by Tom Lane's avatar Tom Lane

Improve ExecEvalVar's handling of whole-row variables in cases where the

rowtype contains dropped columns.  Sometimes the input tuple will be formed
from a select targetlist in which dropped columns are filled with a NULL
of an arbitrary type (the planner typically uses INT4, since it can't tell
what type the dropped column really was).  So we need to relax the rowtype
compatibility check to not insist on physical compatibility if the actual
column value is NULL.

In principle we might need to do this for functions returning composite
types, too (see tupledesc_match()).  In practice there doesn't seem to be
a bug there, probably because the function will be using the same cached
rowtype descriptor as the caller.  Fixing that code path would require
significant rearrangement, so I left it alone for now.

Per complaint from Filip Rembialkowski.
parent ccaad191
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.260 2010/01/09 20:46:19 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.261 2010/01/11 15:31:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -591,17 +591,23 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, ...@@ -591,17 +591,23 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
/* /*
* We really only care about number of attributes and data type. * We really only care about number of attributes and data type.
* Also, we can ignore type mismatch on columns that are dropped * Also, we can ignore type mismatch on columns that are dropped
* in the destination type, so long as the physical storage * in the destination type, so long as (1) the physical storage
* matches. This is helpful in some cases involving out-of-date * matches or (2) the actual column value is NULL. Case (1) is
* cached plans. Also, we have to allow the case that the slot * helpful in some cases involving out-of-date cached plans, while
* has more columns than the Var's type, because we might be * case (2) is expected behavior in situations such as an INSERT
* looking at the output of a subplan that includes resjunk * into a table with dropped columns (the planner typically
* columns. (XXX it would be nice to verify that the extra * generates an INT4 NULL regardless of the dropped column type).
* columns are all marked resjunk, but we haven't got access to * If we find a dropped column and cannot verify that case (1)
* the subplan targetlist here...) Resjunk columns should always * holds, we have to use ExecEvalWholeRowSlow to check (2) for
* be at the end of a targetlist, so it's sufficient to ignore * each row. Also, we have to allow the case that the slot has
* them here; but we need to use ExecEvalWholeRowSlow to get rid * more columns than the Var's type, because we might be looking
* of them in the eventual output tuples. * 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); var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
...@@ -615,7 +621,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, ...@@ -615,7 +621,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
slot_tupdesc->natts, slot_tupdesc->natts,
var_tupdesc->natts))); var_tupdesc->natts)));
else if (var_tupdesc->natts < slot_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++) for (i = 0; i < var_tupdesc->natts; i++)
{ {
...@@ -635,11 +641,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, ...@@ -635,11 +641,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
if (vattr->attlen != sattr->attlen || if (vattr->attlen != sattr->attlen ||
vattr->attalign != sattr->attalign) vattr->attalign != sattr->attalign)
ereport(ERROR, needslow = true; /* need runtime check for null */
(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)));
} }
ReleaseTupleDesc(var_tupdesc); ReleaseTupleDesc(var_tupdesc);
...@@ -766,7 +768,7 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext, ...@@ -766,7 +768,7 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecEvalWholeRowSlow * 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. * we can't just copy the subplan's output.
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
...@@ -779,6 +781,7 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, ...@@ -779,6 +781,7 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
HeapTuple tuple; HeapTuple tuple;
TupleDesc var_tupdesc; TupleDesc var_tupdesc;
HeapTupleHeader dtuple; HeapTupleHeader dtuple;
int i;
if (isDone) if (isDone)
*isDone = ExprSingleResult; *isDone = ExprSingleResult;
...@@ -802,18 +805,38 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, ...@@ -802,18 +805,38 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
} }
/* /*
* Currently, the only case handled here is stripping of trailing resjunk * Currently, the only data modification case handled here is stripping of
* fields, which we do in a slightly chintzy way by just adjusting the * trailing resjunk fields, which we do in a slightly chintzy way by just
* tuple's natts header field. Possibly there will someday be a need for * adjusting the tuple's natts header field. Possibly there will someday
* more-extensive rearrangements, in which case it'd be worth * be a need for more-extensive rearrangements, in which case we'd
* disassembling and reassembling the tuple (perhaps use a JunkFilter for * probably use tupconvert.c.
* that?)
*/ */
Assert(variable->vartype != RECORDOID); Assert(variable->vartype != RECORDOID);
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
tuple = ExecFetchSlotTuple(slot); 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 * 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 * overhead fields, which are not set in on-disk tuples; not to mention
...@@ -826,7 +849,6 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, ...@@ -826,7 +849,6 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
HeapTupleHeaderSetTypeId(dtuple, variable->vartype); HeapTupleHeaderSetTypeId(dtuple, variable->vartype);
HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod);
Assert(HeapTupleHeaderGetNatts(dtuple) >= var_tupdesc->natts);
HeapTupleHeaderSetNatts(dtuple, var_tupdesc->natts); HeapTupleHeaderSetNatts(dtuple, var_tupdesc->natts);
ReleaseTupleDesc(var_tupdesc); ReleaseTupleDesc(var_tupdesc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment