diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index cfea7e160c2..f06c7367892 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -64,6 +64,15 @@ typedef struct ExprSetupInfo AttrNumber last_scan; AttrNumber last_old; AttrNumber last_new; + /* + * All attribute numbers fetched from scan tuple slots, for use by + * slot_gettargetattr(). Unlike most Bitmapsets of attribute numbers + * elsewhere in the codebase (e.g. pull_varattnos()), this is *not* + * offset by FirstLowInvalidHeapAttributeNumber: members are plain + * zero-based tts_values/tts_isnull array indexes (attnum - 1). System + * columns never appear here, since FETCHSOME steps never fetch them. + */ + Bitmapset *all_scan_attrs; /* MULTIEXPR SubPlan nodes appearing in the expression: */ List *multiexpr_subplans; } ExprSetupInfo; @@ -557,7 +566,7 @@ ExecBuildUpdateProjection(List *targetList, int nAssignableCols; bool sawJunk; Bitmapset *assignedCols; - ExprSetupInfo deform = {0, 0, 0, 0, 0, NIL}; + ExprSetupInfo deform = {0, 0, 0, 0, 0, NULL, NIL}; ExprEvalStep scratch = {0}; int outerattnum; ListCell *lc, @@ -2875,7 +2884,7 @@ ExecInitSubPlanExpr(SubPlan *subplan, static void ExecCreateExprSetupSteps(ExprState *state, Node *node) { - ExprSetupInfo info = {0, 0, 0, 0, 0, NIL}; + ExprSetupInfo info = {0, 0, 0, 0, 0, NULL, NIL}; /* Prescan to find out what we need. */ expr_setup_walker(node, &info); @@ -2905,6 +2914,7 @@ ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info) { scratch.opcode = EEOP_INNER_FETCHSOME; scratch.d.fetch.last_var = info->last_inner; + scratch.d.fetch.all_vars = NULL; scratch.d.fetch.fixed = false; scratch.d.fetch.kind = NULL; scratch.d.fetch.known_desc = NULL; @@ -2915,6 +2925,7 @@ ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info) { scratch.opcode = EEOP_OUTER_FETCHSOME; scratch.d.fetch.last_var = info->last_outer; + scratch.d.fetch.all_vars = NULL; scratch.d.fetch.fixed = false; scratch.d.fetch.kind = NULL; scratch.d.fetch.known_desc = NULL; @@ -2925,6 +2936,7 @@ ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info) { scratch.opcode = EEOP_SCAN_FETCHSOME; scratch.d.fetch.last_var = info->last_scan; + scratch.d.fetch.all_vars = info->all_scan_attrs; scratch.d.fetch.fixed = false; scratch.d.fetch.kind = NULL; scratch.d.fetch.known_desc = NULL; @@ -2935,6 +2947,7 @@ ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info) { scratch.opcode = EEOP_OLD_FETCHSOME; scratch.d.fetch.last_var = info->last_old; + scratch.d.fetch.all_vars = NULL; scratch.d.fetch.fixed = false; scratch.d.fetch.kind = NULL; scratch.d.fetch.known_desc = NULL; @@ -2945,6 +2958,7 @@ ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info) { scratch.opcode = EEOP_NEW_FETCHSOME; scratch.d.fetch.last_var = info->last_new; + scratch.d.fetch.all_vars = NULL; scratch.d.fetch.fixed = false; scratch.d.fetch.kind = NULL; scratch.d.fetch.known_desc = NULL; @@ -3001,6 +3015,8 @@ expr_setup_walker(Node *node, ExprSetupInfo *info) { case VAR_RETURNING_DEFAULT: info->last_scan = Max(info->last_scan, attnum); + if (attnum > 0) + info->all_scan_attrs = bms_add_member(info->all_scan_attrs, attnum - 1); break; case VAR_RETURNING_OLD: info->last_old = Max(info->last_old, attnum); @@ -3675,7 +3691,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase, PlanState *parent = &aggstate->ss.ps; ExprEvalStep scratch = {0}; bool isCombine = DO_AGGSPLIT_COMBINE(aggstate->aggsplit); - ExprSetupInfo deform = {0, 0, 0, 0, 0, NIL}; + ExprSetupInfo deform = {0, 0, 0, 0, 0, NULL, NIL}; state->expr = (Expr *) aggstate; state->parent = parent; diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 9bc23cb16fa..da973bf62e0 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -663,7 +663,8 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) { CheckOpSlotCompatibility(op, scanslot); - slot_getsomeattrs(scanslot, op->d.fetch.last_var); + if (!slot_gettargetattr(scanslot, op->d.fetch.all_vars)) + slot_getsomeattrs(scanslot, op->d.fetch.last_var); EEO_NEXT(); } @@ -722,7 +723,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) /* See EEOP_INNER_VAR comments */ - Assert(attnum >= 0 && attnum < scanslot->tts_nvalid); + Assert(attnum >= 0 && slot_is_attr_valid(scanslot, attnum)); *op->resvalue = scanslot->tts_values[attnum]; *op->resnull = scanslot->tts_isnull[attnum]; @@ -836,7 +837,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) * We do not need CheckVarSlotCompatibility here; that was taken * care of at compilation time. But see EEOP_INNER_VAR comments. */ - Assert(attnum >= 0 && attnum < scanslot->tts_nvalid); + Assert(attnum >= 0 && slot_is_attr_valid(scanslot, attnum)); Assert(resultnum >= 0 && resultnum < resultslot->tts_tupleDescriptor->natts); resultslot->tts_values[resultnum] = scanslot->tts_values[attnum]; resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum]; diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 3ad983c7fa5..cab0253c991 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -1291,7 +1291,10 @@ const TupleTableSlotOps TTSOpsVirtual = { .get_heap_tuple = NULL, .get_minimal_tuple = NULL, .copy_heap_tuple = tts_virtual_copy_heap_tuple, - .copy_minimal_tuple = tts_virtual_copy_minimal_tuple + .copy_minimal_tuple = tts_virtual_copy_minimal_tuple, + + .gettargetattr = NULL, + .is_attr_valid = NULL }; const TupleTableSlotOps TTSOpsHeapTuple = { @@ -1309,7 +1312,10 @@ const TupleTableSlotOps TTSOpsHeapTuple = { /* A heap tuple table slot can not "own" a minimal tuple. */ .get_minimal_tuple = NULL, .copy_heap_tuple = tts_heap_copy_heap_tuple, - .copy_minimal_tuple = tts_heap_copy_minimal_tuple + .copy_minimal_tuple = tts_heap_copy_minimal_tuple, + + .gettargetattr = NULL, + .is_attr_valid = NULL }; const TupleTableSlotOps TTSOpsMinimalTuple = { @@ -1327,7 +1333,10 @@ const TupleTableSlotOps TTSOpsMinimalTuple = { .get_heap_tuple = NULL, .get_minimal_tuple = tts_minimal_get_minimal_tuple, .copy_heap_tuple = tts_minimal_copy_heap_tuple, - .copy_minimal_tuple = tts_minimal_copy_minimal_tuple + .copy_minimal_tuple = tts_minimal_copy_minimal_tuple, + + .gettargetattr = NULL, + .is_attr_valid = NULL }; const TupleTableSlotOps TTSOpsBufferHeapTuple = { @@ -1345,7 +1354,10 @@ const TupleTableSlotOps TTSOpsBufferHeapTuple = { /* A buffer heap tuple table slot can not "own" a minimal tuple. */ .get_minimal_tuple = NULL, .copy_heap_tuple = tts_buffer_heap_copy_heap_tuple, - .copy_minimal_tuple = tts_buffer_heap_copy_minimal_tuple + .copy_minimal_tuple = tts_buffer_heap_copy_minimal_tuple, + + .gettargetattr = NULL, + .is_attr_valid = NULL }; @@ -2199,6 +2211,22 @@ slot_getsomeattrs_int(TupleTableSlot *slot, int attnum) */ } +/* + * slot_gettargetattr - fetch exactly the given (possibly sparse) set of + * attributes, for slot types that support it (see TupleTableSlotOps). + * + * Returns false, doing nothing, if the slot type has no gettargetattr + * callback, so the caller can fall back to slot_getsomeattrs(). + */ +bool +slot_gettargetattr(TupleTableSlot *slot, Bitmapset *attrs) +{ + if (slot->tts_ops->gettargetattr == NULL) + return false; + + return slot->tts_ops->gettargetattr(slot, attrs); +} + /* ---------------------------------------------------------------- * ExecTypeFromTL * diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index c61b3d624d5..ca4657f1282 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -329,6 +329,8 @@ typedef struct ExprEvalStep TupleDesc known_desc; /* type of slot, can only be relied upon if fixed is set */ const TupleTableSlotOps *kind; + /* all att numbers to fetch (if NULL, use `last_var`) */ + Bitmapset *all_vars; } fetch; /* for EEOP_INNER/OUTER/SCAN/OLD/NEW_[SYS]VAR */ diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index 3db6c9c9bd0..f062620c475 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -17,6 +17,7 @@ #include "access/htup.h" #include "access/sysattr.h" #include "access/tupdesc.h" +#include "nodes/bitmapset.h" #include "storage/buf.h" /*---------- @@ -123,7 +124,13 @@ typedef struct TupleTableSlot #define FIELDNO_TUPLETABLESLOT_FLAGS 1 uint16 tts_flags; /* Boolean states */ #define FIELDNO_TUPLETABLESLOT_NVALID 2 - AttrNumber tts_nvalid; /* # of valid values in tts_values */ + /* + * # of valid values in tts_values. Entry in tts_values with index + * below tts_nvalid is guaranteed to be valid. But other entries in + * tts_values *may* be valid (if fetched via slot_gettargetattr()) and + * their validity can be checked via slot_is_attr_valid(). + */ + AttrNumber tts_nvalid; const TupleTableSlotOps *const tts_ops; /* implementation of slot */ #define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4 TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ @@ -239,6 +246,20 @@ struct TupleTableSlotOps * with the minimal tuple without the need for an additional allocation. */ MinimalTuple (*copy_minimal_tuple) (TupleTableSlot *slot, Size extra); + + /* + * Fill up target entries of tts_values and tts_isnull arrays with + * values from the tuple contained in the slot. Returns false if the + * callback declines to service this particular call (e.g. because the slot + * isn't yet associated with live storage); caller falls back to + * slot_getsomeattrs(). + */ + bool (*gettargetattr) (TupleTableSlot *slot, Bitmapset *attrs); + + /* + * Check if value for attnum in tts_values and tts_isnull arrays is valid. + */ + bool (*is_attr_valid) (TupleTableSlot *slot, int attnum); }; /* @@ -364,6 +385,7 @@ extern Datum ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot); extern void slot_getmissingattrs(TupleTableSlot *slot, int startAttNum, int lastAttNum); extern void slot_getsomeattrs_int(TupleTableSlot *slot, int attnum); +extern bool slot_gettargetattr(TupleTableSlot *slot, Bitmapset *attrs); #ifndef FRONTEND @@ -392,6 +414,20 @@ slot_getallattrs(TupleTableSlot *slot) slot_getsomeattrs(slot, slot->tts_tupleDescriptor->natts); } +/* + * This function checks if Datum/isnull array value for attnum is valid. + */ +static inline bool +slot_is_attr_valid(TupleTableSlot *slot, int attnum) +{ + if (slot->tts_nvalid > attnum) + return true; + + if (slot->tts_ops->is_attr_valid) + return slot->tts_ops->is_attr_valid(slot, attnum); + + return false; +} /* * slot_attisnull