88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.220 2007/06/11 22:22 :40 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.221 2007/08/31 18:33 :40 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -68,6 +68,8 @@ static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
6868bool * isNull ,ExprDoneCond * isDone );
6969static Datum ExecEvalWholeRowVar (ExprState * exprstate ,ExprContext * econtext ,
7070bool * isNull ,ExprDoneCond * isDone );
71+ static Datum ExecEvalWholeRowSlow (ExprState * exprstate ,ExprContext * econtext ,
72+ bool * isNull ,ExprDoneCond * isDone );
7173static Datum ExecEvalConst (ExprState * exprstate ,ExprContext * econtext ,
7274bool * isNull ,ExprDoneCond * isDone );
7375static Datum ExecEvalParam (ExprState * exprstate ,ExprContext * econtext ,
@@ -438,7 +440,8 @@ ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext,
438440 *
439441 * Note: ExecEvalVar is executed only the first time through in a given plan;
440442 * it changes the ExprState's function pointer to pass control directly to
441- * ExecEvalScalarVar or ExecEvalWholeRowVar after making one-time checks.
443+ * ExecEvalScalarVar, ExecEvalWholeRowVar, or ExecEvalWholeRowSlow after
444+ * making one-time checks.
442445 * ----------------------------------------------------------------
443446 */
444447static Datum
@@ -544,6 +547,7 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
544547 * the actual tuple type is compatible with it.
545548 */
546549TupleDesc slot_tupdesc = slot -> tts_tupleDescriptor ;
550+ bool needslow = false;
547551
548552if (variable -> vartype == RECORDOID )
549553{
@@ -561,16 +565,26 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
561565 * Also, we can ignore type mismatch on columns that are dropped
562566 * in the destination type, so long as the physical storage
563567 * matches. This is helpful in some cases involving out-of-date
564- * cached plans.
568+ * cached plans. Also, we have to allow the case that the slot
569+ * has more columns than the Var's type, because we might be
570+ * looking at the output of a subplan that includes resjunk
571+ * columns. (XXX it would be nice to verify that the extra
572+ * columns are all marked resjunk, but we haven't got access to
573+ * the subplan targetlist here...) Resjunk columns should always
574+ * be at the end of a targetlist, so it's sufficient to ignore
575+ * them here; but we need to use ExecEvalWholeRowSlow to get
576+ * rid of them in the eventual output tuples.
565577 */
566578var_tupdesc = lookup_rowtype_tupdesc (variable -> vartype ,-1 );
567579
568- if (var_tupdesc -> natts != slot_tupdesc -> natts )
580+ if (var_tupdesc -> natts > slot_tupdesc -> natts )
569581ereport (ERROR ,
570582(errcode (ERRCODE_DATATYPE_MISMATCH ),
571583errmsg ("table row type and query-specified row type do not match" ),
572584errdetail ("Table row contains %d attributes, but query expects %d." ,
573585slot_tupdesc -> natts ,var_tupdesc -> natts )));
586+ else if (var_tupdesc -> natts < slot_tupdesc -> natts )
587+ needslow = true;
574588
575589for (i = 0 ;i < var_tupdesc -> natts ;i ++ )
576590{
@@ -601,7 +615,10 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
601615}
602616
603617/* Skip the checking on future executions of node */
604- exprstate -> evalfunc = ExecEvalWholeRowVar ;
618+ if (needslow )
619+ exprstate -> evalfunc = ExecEvalWholeRowSlow ;
620+ else
621+ exprstate -> evalfunc = ExecEvalWholeRowVar ;
605622
606623/* Fetch the value */
607624return ExecEvalWholeRowVar (exprstate ,econtext ,isNull ,isDone );
@@ -698,6 +715,60 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
698715return PointerGetDatum (dtuple );
699716}
700717
718+ /* ----------------------------------------------------------------
719+ *ExecEvalWholeRowSlow
720+ *
721+ *Returns a Datum for a whole-row variable, in the "slow" case where
722+ *we can't just copy the subplan's output.
723+ * ----------------------------------------------------------------
724+ */
725+ static Datum
726+ ExecEvalWholeRowSlow (ExprState * exprstate ,ExprContext * econtext ,
727+ bool * isNull ,ExprDoneCond * isDone )
728+ {
729+ Var * variable = (Var * )exprstate -> expr ;
730+ TupleTableSlot * slot = econtext -> ecxt_scantuple ;
731+ HeapTuple tuple ;
732+ TupleDesc var_tupdesc ;
733+ HeapTupleHeader dtuple ;
734+
735+ if (isDone )
736+ * isDone = ExprSingleResult ;
737+ * isNull = false;
738+
739+ /*
740+ * Currently, the only case handled here is stripping of trailing
741+ * resjunk fields, which we do in a slightly chintzy way by just
742+ * adjusting the tuple's natts header field. Possibly there will someday
743+ * be a need for more-extensive rearrangements, in which case it'd
744+ * be worth disassembling and reassembling the tuple (perhaps use a
745+ * JunkFilter for that?)
746+ */
747+ Assert (variable -> vartype != RECORDOID );
748+ var_tupdesc = lookup_rowtype_tupdesc (variable -> vartype ,-1 );
749+
750+ tuple = ExecFetchSlotTuple (slot );
751+
752+ /*
753+ * We have to make a copy of the tuple so we can safely insert the Datum
754+ * overhead fields, which are not set in on-disk tuples; not to mention
755+ * fooling with its natts field.
756+ */
757+ dtuple = (HeapTupleHeader )palloc (tuple -> t_len );
758+ memcpy ((char * )dtuple , (char * )tuple -> t_data ,tuple -> t_len );
759+
760+ HeapTupleHeaderSetDatumLength (dtuple ,tuple -> t_len );
761+ HeapTupleHeaderSetTypeId (dtuple ,variable -> vartype );
762+ HeapTupleHeaderSetTypMod (dtuple ,variable -> vartypmod );
763+
764+ Assert (HeapTupleHeaderGetNatts (dtuple ) >=var_tupdesc -> natts );
765+ HeapTupleHeaderSetNatts (dtuple ,var_tupdesc -> natts );
766+
767+ ReleaseTupleDesc (var_tupdesc );
768+
769+ return PointerGetDatum (dtuple );
770+ }
771+
701772/* ----------------------------------------------------------------
702773 *ExecEvalConst
703774 *