@ -20,7 +20,7 @@
* ExecProject - form a new tuple by projecting the given tuple
*
* NOTES
* The more heavily used ExecEvalExpr routines , such as ExecEvalVar ( ) ,
* The more heavily used ExecEvalExpr routines , such as ExecEvalScalarVar ,
* are hotspots . Making these faster will speed up the entire system .
*
* ExecProject ( ) is used to make tuple projections . Rather then
@ -68,13 +68,18 @@ static Datum ExecEvalAggref(AggrefExprState *aggref,
static Datum ExecEvalWindowFunc ( WindowFuncExprState * wfunc ,
ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone ) ;
static Datum ExecEvalVar ( ExprState * exprstate , ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone ) ;
static Datum ExecEvalScalarVar ( ExprState * exprstate , ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone ) ;
static Datum ExecEvalWholeRowVar ( ExprState * exprstate , ExprContext * econtext ,
static Datum ExecEvalScalarVarFast ( ExprState * exprstate , ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone ) ;
static Datum ExecEvalWholeRowVar ( WholeRowVarExprState * wrvstate ,
ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone ) ;
static Datum ExecEvalWholeRowSlow ( ExprState * exprstate , ExprContext * econtext ,
static Datum ExecEvalWholeRowFast ( WholeRowVarExprState * wrvstate ,
ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone ) ;
static Datum ExecEvalWholeRowSlow ( WholeRowVarExprState * wrvstate ,
ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone ) ;
static Datum ExecEvalConst ( ExprState * exprstate , ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone ) ;
@ -553,20 +558,19 @@ ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext,
}
/* ----------------------------------------------------------------
* ExecEvalVar
* ExecEvalScalar Var
*
* Returns a Datum whose value is the value of a range
* variable with respect to given expression context .
* Returns a Datum whose value is the value of a scalar ( not whole - row )
* range variable with respect to given expression context .
*
* 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 , ExecEvalWholeRowVar , or ExecEvalWholeRowSlow after
* making one - time checks .
* Note : ExecEvalScalarVar is executed only the first time through in a given
* plan ; it changes the ExprState ' s function pointer to pass control directly
* to ExecEvalScalarVarFast after making one - time checks .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static Datum
ExecEvalVar ( ExprState * exprstate , ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone )
ExecEvalScalar Var ( ExprState * exprstate , ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone )
{
Var * variable = ( Var * ) exprstate - > expr ;
TupleTableSlot * slot ;
@ -596,162 +600,65 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
attnum = variable - > varattno ;
if ( attnum ! = InvalidAttrNumber )
{
/*
* Scalar variable case .
*
* If it ' s a user attribute , check validity ( bogus system attnums will
* be caught inside slot_getattr ) . What we have to check for here is
* the possibility of an attribute having been changed in type since
* the plan tree was created . Ideally the plan would get invalidated
* and not re - used , but until that day arrives , we need defenses .
* Fortunately it ' s sufficient to check once on the first time
* through .
*
* Note : we allow a reference to a dropped attribute . slot_getattr
* will force a NULL result in such cases .
*
* Note : ideally we ' d check typmod as well as typid , but that seems
* impractical at the moment : in many cases the tupdesc will have been
* generated by ExecTypeFromTL ( ) , and that can ' t guarantee to generate
* an accurate typmod in all cases , because some expression node types
* don ' t carry typmod .
*/
if ( attnum > 0 )
{
TupleDesc slot_tupdesc = slot - > tts_tupleDescriptor ;
Form_pg_attribute attr ;
/* This was checked by ExecInitExpr */
Assert ( attnum ! = InvalidAttrNumber ) ;
if ( attnum > slot_tupdesc - > natts ) /* should never happen */
elog ( ERROR , " attribute number %d exceeds number of columns %d " ,
attnum , slot_tupdesc - > natts ) ;
attr = slot_tupdesc - > attrs [ attnum - 1 ] ;
/* can't check type if dropped, since atttypid is probably 0 */
if ( ! attr - > attisdropped )
{
if ( variable - > vartype ! = attr - > atttypid )
ereport ( ERROR ,
( errmsg ( " attribute %d has wrong type " , attnum ) ,
errdetail ( " Table has type %s, but query expects %s. " ,
format_type_be ( attr - > atttypid ) ,
format_type_be ( variable - > vartype ) ) ) ) ;
}
}
/* Skip the checking on future executions of node */
exprstate - > evalfunc = ExecEvalScalarVar ;
/* Fetch the value from the slot */
return slot_getattr ( slot , attnum , isNull ) ;
}
else
/*
* If it ' s a user attribute , check validity ( bogus system attnums will be
* caught inside slot_getattr ) . What we have to check for here is the
* possibility of an attribute having been changed in type since the plan
* tree was created . Ideally the plan will get invalidated and not
* re - used , but just in case , we keep these defenses . Fortunately it ' s
* sufficient to check once on the first time through .
*
* Note : we allow a reference to a dropped attribute . slot_getattr will
* force a NULL result in such cases .
*
* Note : ideally we ' d check typmod as well as typid , but that seems
* impractical at the moment : in many cases the tupdesc will have been
* generated by ExecTypeFromTL ( ) , and that can ' t guarantee to generate an
* accurate typmod in all cases , because some expression node types don ' t
* carry typmod .
*/
if ( attnum > 0 )
{
/*
* Whole - row variable .
*
* If it ' s a RECORD Var , we ' ll use the slot ' s type ID info . It ' s
* likely that the slot ' s type is also RECORD ; if so , make sure it ' s
* been " blessed " , so that the Datum can be interpreted later .
*
* If the Var identifies a named composite type , we must check that
* the actual tuple type is compatible with it .
*/
TupleDesc slot_tupdesc = slot - > tts_tupleDescriptor ;
bool needslow = false ;
Form_pg_attribute attr ;
if ( variable - > vartype = = RECORDOID )
{
if ( slot_tupdesc - > tdtypeid = = RECORDOID & &
slot_tupdesc - > tdtypmod < 0 )
assign_record_type_typmod ( slot_tupdesc ) ;
}
else
{
TupleDesc var_tupdesc ;
int i ;
if ( attnum > slot_tupdesc - > natts ) /* should never happen */
elog ( ERROR , " attribute number %d exceeds number of columns %d " ,
attnum , slot_tupdesc - > natts ) ;
/*
* 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 ( 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 ) ;
attr = slot_tupdesc - > attrs [ attnum - 1 ] ;
if ( var_tupdesc - > natts > slot_tupdesc - > natts )
/* can't check type if dropped, since atttypid is probably 0 */
if ( ! attr - > attisdropped )
{
if ( variable - > vartype ! = attr - > atttypid )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " table row type and query-specified row type do not match " ) ,
errdetail_plural ( " Table row contains %d attribute, but query expects %d. " ,
" Table row contains %d attributes, but query expects %d. " ,
slot_tupdesc - > natts ,
slot_tupdesc - > natts ,
var_tupdesc - > natts ) ) ) ;
else if ( var_tupdesc - > natts < slot_tupdesc - > natts )
needslow = true ; /* need to trim trailing atts */
for ( i = 0 ; i < var_tupdesc - > natts ; i + + )
{
Form_pg_attribute vattr = var_tupdesc - > attrs [ i ] ;
Form_pg_attribute sattr = slot_tupdesc - > attrs [ i ] ;
if ( vattr - > atttypid = = sattr - > atttypid )
continue ; /* no worries */
if ( ! vattr - > attisdropped )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " table row type and query-specified row type do not match " ) ,
errdetail ( " Table has type %s at ordinal position %d, but query expects %s. " ,
format_type_be ( sattr - > atttypid ) ,
i + 1 ,
format_type_be ( vattr - > atttypid ) ) ) ) ;
if ( vattr - > attlen ! = sattr - > attlen | |
vattr - > attalign ! = sattr - > attalign )
needslow = true ; /* need runtime check for null */
}
ReleaseTupleDesc ( var_tupdesc ) ;
( errmsg ( " attribute %d has wrong type " , attnum ) ,
errdetail ( " Table has type %s, but query expects %s. " ,
format_type_be ( attr - > atttypid ) ,
format_type_be ( variable - > vartype ) ) ) ) ;
}
}
/* Skip the checking on future executions of node */
if ( needslow )
exprstate - > evalfunc = ExecEvalWholeRowSlow ;
else
exprstate - > evalfunc = ExecEvalWholeRowVar ;
/* Skip the checking on future executions of node */
exprstate - > evalfunc = ExecEvalScalarVarFast ;
/* Fetch the value */
return ( * exprstate - > evalfunc ) ( exprstate , econtext , isNull , isDone ) ;
}
/* Fetch the value from the slot */
return slot_getattr ( slot , attnum , isNull ) ;
}
/* ----------------------------------------------------------------
* ExecEvalScalarVar
* ExecEvalScalarVarFast
*
* Returns a Datum for a scalar variable .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static Datum
ExecEvalScalarVar ( ExprState * exprstate , ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone )
ExecEvalScalarVarFast ( ExprState * exprstate , ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone )
{
Var * variable = ( Var * ) exprstate - > expr ;
TupleTableSlot * slot ;
@ -788,14 +695,204 @@ ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
/* ----------------------------------------------------------------
* ExecEvalWholeRowVar
*
* Returns a Datum for a whole - row variable .
* Returns a Datum whose value is the value of a whole - row range
* variable with respect to given expression context .
*
* Note : ExecEvalWholeRowVar is executed only the first time through in a
* given plan ; it changes the ExprState ' s function pointer to pass control
* directly to ExecEvalWholeRowFast or ExecEvalWholeRowSlow after making
* one - time checks .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static Datum
ExecEvalWholeRowVar ( ExprState * exprstate , ExprContext * econtext ,
ExecEvalWholeRowVar ( WholeRowVar ExprState * wrv state, ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone )
{
Var * variable = ( Var * ) exprstate - > expr ;
Var * variable = ( Var * ) wrvstate - > xprstate . expr ;
TupleTableSlot * slot ;
TupleDesc slot_tupdesc ;
bool needslow = false ;
if ( isDone )
* isDone = ExprSingleResult ;
/* This was checked by ExecInitExpr */
Assert ( variable - > varattno = = InvalidAttrNumber ) ;
/* Get the input slot we want */
switch ( variable - > varno )
{
case INNER_VAR : /* get the tuple from the inner node */
slot = econtext - > ecxt_innertuple ;
break ;
case OUTER_VAR : /* get the tuple from the outer node */
slot = econtext - > ecxt_outertuple ;
break ;
/* INDEX_VAR is handled by default case */
default : /* get the tuple from the relation being
* scanned */
slot = econtext - > ecxt_scantuple ;
break ;
}
/*
* If the input tuple came from a subquery , it might contain " resjunk "
* columns ( such as GROUP BY or ORDER BY columns ) , which we don ' t want to
* keep in the whole - row result . We can get rid of such columns by
* passing the tuple through a JunkFilter - - - but to make one , we have to
* lay our hands on the subquery ' s targetlist . Fortunately , there are not
* very many cases where this can happen , and we can identify all of them
* by examining our parent PlanState . We assume this is not an issue in
* standalone expressions that don ' t have parent plans . ( Whole - row Vars
* can occur in such expressions , but they will always be referencing
* table rows . )
*/
if ( wrvstate - > parent )
{
PlanState * subplan = NULL ;
switch ( nodeTag ( wrvstate - > parent ) )
{
case T_SubqueryScanState :
subplan = ( ( SubqueryScanState * ) wrvstate - > parent ) - > subplan ;
break ;
case T_CteScanState :
subplan = ( ( CteScanState * ) wrvstate - > parent ) - > cteplanstate ;
break ;
default :
break ;
}
if ( subplan )
{
bool junk_filter_needed = false ;
ListCell * tlist ;
/* Detect whether subplan tlist actually has any junk columns */
foreach ( tlist , subplan - > plan - > targetlist )
{
TargetEntry * tle = ( TargetEntry * ) lfirst ( tlist ) ;
if ( tle - > resjunk )
{
junk_filter_needed = true ;
break ;
}
}
/* If so, build the junkfilter in the query memory context */
if ( junk_filter_needed )
{
MemoryContext oldcontext ;
oldcontext = MemoryContextSwitchTo ( econtext - > ecxt_per_query_memory ) ;
wrvstate - > wrv_junkFilter =
ExecInitJunkFilter ( subplan - > plan - > targetlist ,
ExecGetResultType ( subplan ) - > tdhasoid ,
ExecInitExtraTupleSlot ( wrvstate - > parent - > state ) ) ;
MemoryContextSwitchTo ( oldcontext ) ;
}
}
}
/* Apply the junkfilter if any */
if ( wrvstate - > wrv_junkFilter ! = NULL )
slot = ExecFilterJunk ( wrvstate - > wrv_junkFilter , slot ) ;
slot_tupdesc = slot - > tts_tupleDescriptor ;
/*
* If it ' s a RECORD Var , we ' ll use the slot ' s type ID info . It ' s likely
* that the slot ' s type is also RECORD ; if so , make sure it ' s been
* " blessed " , so that the Datum can be interpreted later .
*
* If the Var identifies a named composite type , we must check that the
* actual tuple type is compatible with it .
*/
if ( variable - > vartype = = RECORDOID )
{
if ( slot_tupdesc - > tdtypeid = = RECORDOID & &
slot_tupdesc - > tdtypmod < 0 )
assign_record_type_typmod ( slot_tupdesc ) ;
}
else
{
TupleDesc var_tupdesc ;
int i ;
/*
* We really only care about numbers of attributes and data types .
* Also , we can ignore type mismatch on columns that are dropped 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 .
*/
var_tupdesc = lookup_rowtype_tupdesc ( variable - > vartype , - 1 ) ;
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_plural ( " Table row contains %d attribute, but query expects %d. " ,
" Table row contains %d attributes, but query expects %d. " ,
slot_tupdesc - > natts ,
slot_tupdesc - > natts ,
var_tupdesc - > natts ) ) ) ;
for ( i = 0 ; i < var_tupdesc - > natts ; i + + )
{
Form_pg_attribute vattr = var_tupdesc - > attrs [ i ] ;
Form_pg_attribute sattr = slot_tupdesc - > attrs [ i ] ;
if ( vattr - > atttypid = = sattr - > atttypid )
continue ; /* no worries */
if ( ! vattr - > attisdropped )
ereport ( ERROR ,
( errcode ( ERRCODE_DATATYPE_MISMATCH ) ,
errmsg ( " table row type and query-specified row type do not match " ) ,
errdetail ( " Table has type %s at ordinal position %d, but query expects %s. " ,
format_type_be ( sattr - > atttypid ) ,
i + 1 ,
format_type_be ( vattr - > atttypid ) ) ) ) ;
if ( vattr - > attlen ! = sattr - > attlen | |
vattr - > attalign ! = sattr - > attalign )
needslow = true ; /* need runtime check for null */
}
ReleaseTupleDesc ( var_tupdesc ) ;
}
/* Skip the checking on future executions of node */
if ( needslow )
wrvstate - > xprstate . evalfunc = ( ExprStateEvalFunc ) ExecEvalWholeRowSlow ;
else
wrvstate - > xprstate . evalfunc = ( ExprStateEvalFunc ) ExecEvalWholeRowFast ;
/* Fetch the value */
return ( * wrvstate - > xprstate . evalfunc ) ( ( ExprState * ) wrvstate , econtext ,
isNull , isDone ) ;
}
/* ----------------------------------------------------------------
* ExecEvalWholeRowFast
*
* Returns a Datum for a whole - row variable .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static Datum
ExecEvalWholeRowFast ( WholeRowVarExprState * wrvstate , ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone )
{
Var * variable = ( Var * ) wrvstate - > xprstate . expr ;
TupleTableSlot * slot ;
HeapTuple tuple ;
TupleDesc tupleDesc ;
@ -824,6 +921,10 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
break ;
}
/* Apply the junkfilter if any */
if ( wrvstate - > wrv_junkFilter ! = NULL )
slot = ExecFilterJunk ( wrvstate - > wrv_junkFilter , slot ) ;
tuple = ExecFetchSlotTuple ( slot ) ;
tupleDesc = slot - > tts_tupleDescriptor ;
@ -857,17 +958,18 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
/* ----------------------------------------------------------------
* ExecEvalWholeRowSlow
*
* Returns a Datum for a whole - row variable , in the " slow " cases where
* 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 * expr state, ExprContext * econtext ,
ExecEvalWholeRowSlow ( WholeRowVar ExprState * wrv state, ExprContext * econtext ,
bool * isNull , ExprDoneCond * isDone )
{
Var * variable = ( Var * ) expr state- > expr ;
Var * variable = ( Var * ) wrv state- > xprstate . expr ;
TupleTableSlot * slot ;
HeapTuple tuple ;
TupleDesc tupleDesc ;
TupleDesc var_tupdesc ;
HeapTupleHeader dtuple ;
int i ;
@ -895,25 +997,21 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
break ;
}
/*
* 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 ) ;
/* Apply the junkfilter if any */
if ( wrvstate - > wrv_junkFilter ! = NULL )
slot = ExecFilterJunk ( wrvstate - > wrv_junkFilter , slot ) ;
tuple = ExecFetchSlotTuple ( slot ) ;
tupleDesc = slot - > tts_tupleDescriptor ;
Assert ( HeapTupleHeaderGetNatts ( tuple - > t_data ) > = var_tupdesc - > natts ) ;
Assert ( variable - > vartype ! = RECORDOID ) ;
var_tupdesc = lookup_rowtype_tupdesc ( variable - > vartype , - 1 ) ;
/* 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 ] ;
Form_pg_attribute sattr = tupleDesc - > attrs [ i ] ;
if ( ! vattr - > attisdropped )
continue ; /* already checked non-dropped cols */
@ -930,8 +1028,7 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
/*
* 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 .
* overhead fields , which are not set in on - disk tuples .
*/
dtuple = ( HeapTupleHeader ) palloc ( tuple - > t_len ) ;
memcpy ( ( char * ) dtuple , ( char * ) tuple - > t_data , tuple - > t_len ) ;
@ -940,8 +1037,6 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
HeapTupleHeaderSetTypeId ( dtuple , variable - > vartype ) ;
HeapTupleHeaderSetTypMod ( dtuple , variable - > vartypmod ) ;
HeapTupleHeaderSetNatts ( dtuple , var_tupdesc - > natts ) ;
ReleaseTupleDesc ( var_tupdesc ) ;
return PointerGetDatum ( dtuple ) ;
@ -3907,7 +4002,7 @@ ExecEvalFieldSelect(FieldSelectState *fstate,
}
/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
/* As in ExecEvalVar, we should but can't check typmod */
/* As in ExecEvalScalar Var, we should but can't check typmod */
if ( fselect - > resulttype ! = attr - > atttypid )
ereport ( ERROR ,
( errmsg ( " attribute %d has wrong type " , fieldnum ) ,
@ -4236,8 +4331,21 @@ ExecInitExpr(Expr *node, PlanState *parent)
switch ( nodeTag ( node ) )
{
case T_Var :
state = ( ExprState * ) makeNode ( ExprState ) ;
state - > evalfunc = ExecEvalVar ;
/* varattno == InvalidAttrNumber means it's a whole-row Var */
if ( ( ( Var * ) node ) - > varattno = = InvalidAttrNumber )
{
WholeRowVarExprState * wstate = makeNode ( WholeRowVarExprState ) ;
wstate - > parent = parent ;
wstate - > wrv_junkFilter = NULL ;
state = ( ExprState * ) wstate ;
state - > evalfunc = ( ExprStateEvalFunc ) ExecEvalWholeRowVar ;
}
else
{
state = ( ExprState * ) makeNode ( ExprState ) ;
state - > evalfunc = ExecEvalScalarVar ;
}
break ;
case T_Const :
state = ( ExprState * ) makeNode ( ExprState ) ;