mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-30 19:00:29 +08:00
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.
This commit is contained in:
parent
e257e6095f
commit
2e5e715770
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
bool *isNull, ExprDoneCond *isDone);
|
||||||
static Datum ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
|
static Datum ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
|
||||||
bool *isNull, ExprDoneCond *isDone);
|
bool *isNull, ExprDoneCond *isDone);
|
||||||
|
static Datum ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
|
||||||
|
bool *isNull, ExprDoneCond *isDone);
|
||||||
static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
|
static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
|
||||||
bool *isNull, ExprDoneCond *isDone);
|
bool *isNull, ExprDoneCond *isDone);
|
||||||
static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
|
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;
|
* 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
|
* 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
|
static Datum
|
||||||
@ -533,6 +536,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
|
|||||||
* the actual tuple type is compatible with it.
|
* the actual tuple type is compatible with it.
|
||||||
*/
|
*/
|
||||||
TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
|
TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
|
||||||
|
bool needslow = false;
|
||||||
|
|
||||||
if (variable->vartype == RECORDOID)
|
if (variable->vartype == RECORDOID)
|
||||||
{
|
{
|
||||||
@ -550,16 +554,26 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
|
|||||||
* 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 the physical storage
|
||||||
* matches. This is helpful in some cases involving out-of-date
|
* 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);
|
var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
|
||||||
|
|
||||||
if (var_tupdesc->natts != slot_tupdesc->natts)
|
if (var_tupdesc->natts > slot_tupdesc->natts)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
errmsg("table row type and query-specified row type do not match"),
|
errmsg("table row type and query-specified row type do not match"),
|
||||||
errdetail("Table row contains %d attributes, but query expects %d.",
|
errdetail("Table row contains %d attributes, but query expects %d.",
|
||||||
slot_tupdesc->natts, var_tupdesc->natts)));
|
slot_tupdesc->natts, var_tupdesc->natts)));
|
||||||
|
else if (var_tupdesc->natts < slot_tupdesc->natts)
|
||||||
|
needslow = true;
|
||||||
|
|
||||||
for (i = 0; i < var_tupdesc->natts; i++)
|
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 */
|
/* Skip the checking on future executions of node */
|
||||||
exprstate->evalfunc = ExecEvalWholeRowVar;
|
if (needslow)
|
||||||
|
exprstate->evalfunc = ExecEvalWholeRowSlow;
|
||||||
|
else
|
||||||
|
exprstate->evalfunc = ExecEvalWholeRowVar;
|
||||||
|
|
||||||
/* Fetch the value */
|
/* Fetch the value */
|
||||||
return ExecEvalWholeRowVar(exprstate, econtext, isNull, isDone);
|
return ExecEvalWholeRowVar(exprstate, econtext, isNull, isDone);
|
||||||
@ -687,6 +704,60 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
|
|||||||
return PointerGetDatum(dtuple);
|
return PointerGetDatum(dtuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
* 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
|
* ExecEvalConst
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user