diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2007-08-31 18:33:47 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2007-08-31 18:33:47 +0000 |
commit | 2e5e715770f57deadf596c7e800628d08d8d2278 (patch) | |
tree | 5f4e641037171cfc3b9c5e441f89e69bc8171f8b /src | |
parent | e257e6095fac2eebdb1cc5d6c29968486b2b8c56 (diff) | |
download | postgresql-2e5e715770f57deadf596c7e800628d08d8d2278.tar.gz postgresql-2e5e715770f57deadf596c7e800628d08d8d2278.zip |
Extend whole-row Var evaluation to cope with the case that the sub-plan
generating the tuples has resjunk output columns. This is not possible for
simple table scans but can happen when evaluating a whole-row Var for a view.
Per example from Patryk Kordylewski. The problem exists back to 8.0 but
I'm not going to risk back-patching further than 8.2 because of the many
changes in this area.
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/executor/execQual.c | 81 |
1 files changed, 76 insertions, 5 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index b52067910b9..f48e2ad62bf 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.199.2.2 2007/02/06 17:35:27 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.199.2.3 2007/08/31 18:33:47 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -67,6 +67,8 @@ static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext, @@ -427,7 +429,8 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, * * Note: ExecEvalVar is executed only the first time through in a given plan; * it changes the ExprState's function pointer to pass control directly to - * ExecEvalScalarVar or ExecEvalWholeRowVar after making one-time checks. + * ExecEvalScalarVar, ExecEvalWholeRowVar, or ExecEvalWholeRowSlow after + * making one-time checks. * ---------------------------------------------------------------- */ static Datum @@ -533,6 +536,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, * the actual tuple type is compatible with it. */ TupleDesc slot_tupdesc = slot->tts_tupleDescriptor; + bool needslow = false; if (variable->vartype == RECORDOID) { @@ -550,16 +554,26 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, * 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. + * 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. */ var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); - if (var_tupdesc->natts != slot_tupdesc->natts) + if (var_tupdesc->natts > slot_tupdesc->natts) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("table row type and query-specified row type do not match"), errdetail("Table row contains %d attributes, but query expects %d.", slot_tupdesc->natts, var_tupdesc->natts))); + else if (var_tupdesc->natts < slot_tupdesc->natts) + needslow = true; for (i = 0; i < var_tupdesc->natts; i++) { @@ -590,7 +604,10 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, } /* Skip the checking on future executions of node */ - exprstate->evalfunc = ExecEvalWholeRowVar; + if (needslow) + exprstate->evalfunc = ExecEvalWholeRowSlow; + else + exprstate->evalfunc = ExecEvalWholeRowVar; /* Fetch the value */ return ExecEvalWholeRowVar(exprstate, econtext, isNull, isDone); @@ -688,6 +705,60 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext, } /* ---------------------------------------------------------------- + * ExecEvalWholeRowSlow + * + * Returns a Datum for a whole-row variable, in the "slow" case where + * we can't just copy the subplan's output. + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) +{ + Var *variable = (Var *) exprstate->expr; + TupleTableSlot *slot = econtext->ecxt_scantuple; + HeapTuple tuple; + TupleDesc var_tupdesc; + HeapTupleHeader dtuple; + + 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?) + */ + Assert(variable->vartype != RECORDOID); + var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1); + + tuple = ExecFetchSlotTuple(slot); + + /* + * 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 + * fooling with its natts field. + */ + dtuple = (HeapTupleHeader) palloc(tuple->t_len); + memcpy((char *) dtuple, (char *) tuple->t_data, tuple->t_len); + + HeapTupleHeaderSetDatumLength(dtuple, tuple->t_len); + HeapTupleHeaderSetTypeId(dtuple, variable->vartype); + HeapTupleHeaderSetTypMod(dtuple, variable->vartypmod); + + Assert(dtuple->t_natts >= var_tupdesc->natts); + dtuple->t_natts = var_tupdesc->natts; + + ReleaseTupleDesc(var_tupdesc); + + return PointerGetDatum(dtuple); +} + +/* ---------------------------------------------------------------- * ExecEvalConst * * Returns the value of a constant. |