diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index 6b4863b33a..15444cf582 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -1391,7 +1391,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts) { /* Restore state from previous execution */ off = slot->tts_off; - slow = slot->tts_slow; + slow = TTS_SLOW(slot); } tp = (char *) tup + tup->t_hoff; @@ -1452,7 +1452,10 @@ slot_deform_tuple(TupleTableSlot *slot, int natts) */ slot->tts_nvalid = attnum; slot->tts_off = off; - slot->tts_slow = slow; + if (slow) + slot->tts_flags |= TTS_FLAG_SLOW; + else + slot->tts_flags &= ~TTS_FLAG_SLOW; } /* diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index fbbac1c6bf..015752a93f 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -101,11 +101,10 @@ MakeTupleTableSlot(TupleDesc tupleDesc) slot = palloc0(sz); slot->type = T_TupleTableSlot; - slot->tts_isempty = true; - slot->tts_shouldFree = false; - slot->tts_shouldFreeMin = false; + slot->tts_flags |= TTS_FLAG_EMPTY; + if (tupleDesc != NULL) + slot->tts_flags |= TTS_FLAG_FIXED; slot->tts_tuple = NULL; - slot->tts_fixedTupleDescriptor = tupleDesc != NULL; slot->tts_tupleDescriptor = tupleDesc; slot->tts_mcxt = CurrentMemoryContext; slot->tts_buffer = InvalidBuffer; @@ -176,7 +175,7 @@ ExecResetTupleTable(List *tupleTable, /* tuple table */ /* If shouldFree, release memory occupied by the slot itself */ if (shouldFree) { - if (!slot->tts_fixedTupleDescriptor) + if (!TTS_FIXED(slot)) { if (slot->tts_values) pfree(slot->tts_values); @@ -224,7 +223,7 @@ ExecDropSingleTupleTableSlot(TupleTableSlot *slot) ExecClearTuple(slot); if (slot->tts_tupleDescriptor) ReleaseTupleDesc(slot->tts_tupleDescriptor); - if (!slot->tts_fixedTupleDescriptor) + if (!TTS_FIXED(slot)) { if (slot->tts_values) pfree(slot->tts_values); @@ -254,7 +253,7 @@ void ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */ TupleDesc tupdesc) /* new tuple descriptor */ { - Assert(!slot->tts_fixedTupleDescriptor); + Assert(!TTS_FIXED(slot)); /* For safety, make sure slot is empty before changing it */ ExecClearTuple(slot); @@ -325,17 +324,23 @@ ExecStoreHeapTuple(HeapTuple tuple, /* * Free any old physical tuple belonging to the slot. */ - if (slot->tts_shouldFree) + if (TTS_SHOULDFREE(slot)) + { heap_freetuple(slot->tts_tuple); - if (slot->tts_shouldFreeMin) + slot->tts_flags &= ~TTS_FLAG_SHOULDFREE; + } + if (TTS_SHOULDFREEMIN(slot)) + { heap_free_minimal_tuple(slot->tts_mintuple); + slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN; + } /* * Store the new tuple into the specified slot. */ - slot->tts_isempty = false; - slot->tts_shouldFree = shouldFree; - slot->tts_shouldFreeMin = false; + slot->tts_flags &= ~TTS_FLAG_EMPTY; + if (shouldFree) + slot->tts_flags |= TTS_FLAG_SHOULDFREE; slot->tts_tuple = tuple; slot->tts_mintuple = NULL; @@ -382,17 +387,21 @@ ExecStoreBufferHeapTuple(HeapTuple tuple, /* * Free any old physical tuple belonging to the slot. */ - if (slot->tts_shouldFree) + if (TTS_SHOULDFREE(slot)) + { heap_freetuple(slot->tts_tuple); - if (slot->tts_shouldFreeMin) + slot->tts_flags &= ~TTS_FLAG_SHOULDFREE; + } + if (TTS_SHOULDFREEMIN(slot)) + { heap_free_minimal_tuple(slot->tts_mintuple); + slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN; + } /* * Store the new tuple into the specified slot. */ - slot->tts_isempty = false; - slot->tts_shouldFree = false; - slot->tts_shouldFreeMin = false; + slot->tts_flags &= ~TTS_FLAG_EMPTY; slot->tts_tuple = tuple; slot->tts_mintuple = NULL; @@ -442,10 +451,16 @@ ExecStoreMinimalTuple(MinimalTuple mtup, /* * Free any old physical tuple belonging to the slot. */ - if (slot->tts_shouldFree) + if (TTS_SHOULDFREE(slot)) + { heap_freetuple(slot->tts_tuple); - if (slot->tts_shouldFreeMin) + slot->tts_flags &= ~TTS_FLAG_SHOULDFREE; + } + if (TTS_SHOULDFREEMIN(slot)) + { heap_free_minimal_tuple(slot->tts_mintuple); + slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN; + } /* * Drop the pin on the referenced buffer, if there is one. @@ -458,9 +473,9 @@ ExecStoreMinimalTuple(MinimalTuple mtup, /* * Store the new tuple into the specified slot. */ - slot->tts_isempty = false; - slot->tts_shouldFree = false; - slot->tts_shouldFreeMin = shouldFree; + slot->tts_flags &= ~TTS_FLAG_EMPTY; + if (shouldFree) + slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN; slot->tts_tuple = &slot->tts_minhdr; slot->tts_mintuple = mtup; @@ -493,15 +508,19 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */ /* * Free the old physical tuple if necessary. */ - if (slot->tts_shouldFree) + if (TTS_SHOULDFREE(slot)) + { heap_freetuple(slot->tts_tuple); - if (slot->tts_shouldFreeMin) + slot->tts_flags &= ~TTS_FLAG_SHOULDFREE; + } + if (TTS_SHOULDFREEMIN(slot)) + { heap_free_minimal_tuple(slot->tts_mintuple); + slot->tts_flags &= ~TTS_FLAG_SHOULDFREEMIN; + } slot->tts_tuple = NULL; slot->tts_mintuple = NULL; - slot->tts_shouldFree = false; - slot->tts_shouldFreeMin = false; /* * Drop the pin on the referenced buffer, if there is one. @@ -514,7 +533,7 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */ /* * Mark it empty. */ - slot->tts_isempty = true; + slot->tts_flags |= TTS_FLAG_EMPTY; slot->tts_nvalid = 0; return slot; @@ -539,9 +558,9 @@ ExecStoreVirtualTuple(TupleTableSlot *slot) */ Assert(slot != NULL); Assert(slot->tts_tupleDescriptor != NULL); - Assert(slot->tts_isempty); + Assert(TTS_EMPTY(slot)); - slot->tts_isempty = false; + slot->tts_flags &= ~TTS_FLAG_EMPTY; slot->tts_nvalid = slot->tts_tupleDescriptor->natts; return slot; @@ -595,7 +614,7 @@ ExecCopySlotTuple(TupleTableSlot *slot) * sanity checks */ Assert(slot != NULL); - Assert(!slot->tts_isempty); + Assert(!TTS_EMPTY(slot)); /* * If we have a physical tuple (either format) then just copy it. @@ -627,7 +646,7 @@ ExecCopySlotMinimalTuple(TupleTableSlot *slot) * sanity checks */ Assert(slot != NULL); - Assert(!slot->tts_isempty); + Assert(!TTS_EMPTY(slot)); /* * If we have a physical tuple then just copy it. Prefer to copy @@ -675,7 +694,7 @@ ExecFetchSlotTuple(TupleTableSlot *slot) * sanity checks */ Assert(slot != NULL); - Assert(!slot->tts_isempty); + Assert(!TTS_EMPTY(slot)); /* * If we have a regular physical tuple then just return it. @@ -724,7 +743,8 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot) * sanity checks */ Assert(slot != NULL); - Assert(!slot->tts_isempty); + Assert(!TTS_EMPTY(slot)); + /* * If we have a minimal physical tuple (local or not) then just return it. @@ -741,7 +761,7 @@ ExecFetchSlotMinimalTuple(TupleTableSlot *slot) */ oldContext = MemoryContextSwitchTo(slot->tts_mcxt); slot->tts_mintuple = ExecCopySlotMinimalTuple(slot); - slot->tts_shouldFreeMin = true; + slot->tts_flags |= TTS_FLAG_SHOULDFREEMIN; MemoryContextSwitchTo(oldContext); /* @@ -797,13 +817,13 @@ ExecMaterializeSlot(TupleTableSlot *slot) * sanity checks */ Assert(slot != NULL); - Assert(!slot->tts_isempty); + Assert(!TTS_EMPTY(slot)); /* * If we have a regular physical tuple, and it's locally palloc'd, we have * nothing to do. */ - if (slot->tts_tuple && slot->tts_shouldFree) + if (slot->tts_tuple && TTS_SHOULDFREE(slot)) return slot->tts_tuple; /* @@ -815,7 +835,7 @@ ExecMaterializeSlot(TupleTableSlot *slot) */ oldContext = MemoryContextSwitchTo(slot->tts_mcxt); slot->tts_tuple = ExecCopySlotTuple(slot); - slot->tts_shouldFree = true; + slot->tts_flags |= TTS_FLAG_SHOULDFREE; MemoryContextSwitchTo(oldContext); /* @@ -842,7 +862,7 @@ ExecMaterializeSlot(TupleTableSlot *slot) * storage, we must not pfree it now, since callers might have already * fetched datum pointers referencing it.) */ - if (!slot->tts_shouldFreeMin) + if (!TTS_SHOULDFREEMIN(slot)) slot->tts_mintuple = NULL; return slot->tts_tuple; diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 98d8483b72..2413f1f87d 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1080,7 +1080,7 @@ prepare_projection_slot(AggState *aggstate, TupleTableSlot *slot, int currentSet aggstate->grouped_cols = grouped_cols; - if (slot->tts_isempty) + if (TTS_EMPTY(slot)) { /* * Force all values to be NULL if working on an empty input tuple diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 24beb40435..528f58717e 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -699,7 +699,7 @@ ExecDelete(ModifyTableState *mtstate, * RETURNING expressions might reference the tableoid column, so * initialize t_tableOid before evaluating them. */ - if (slot->tts_isempty) + if (TTS_EMPTY(slot)) ExecStoreAllNullTuple(slot); tuple = ExecMaterializeSlot(slot); tuple->t_tableOid = RelationGetRelid(resultRelationDesc); diff --git a/src/backend/jit/llvm/llvmjit_deform.c b/src/backend/jit/llvm/llvmjit_deform.c index 6d7ce21865..59e38d2d95 100644 --- a/src/backend/jit/llvm/llvmjit_deform.c +++ b/src/backend/jit/llvm/llvmjit_deform.c @@ -60,7 +60,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts) LLVMValueRef v_tts_values; LLVMValueRef v_tts_nulls; LLVMValueRef v_slotoffp; - LLVMValueRef v_slowp; + LLVMValueRef v_flagsp; LLVMValueRef v_nvalidp; LLVMValueRef v_nvalid; LLVMValueRef v_maxatt; @@ -168,7 +168,7 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts) "tts_ISNULL"); v_slotoffp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_OFF, ""); - v_slowp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_SLOW, ""); + v_flagsp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_FLAGS, ""); v_nvalidp = LLVMBuildStructGEP(b, v_slot, FIELDNO_TUPLETABLESLOT_NVALID, ""); v_tupleheaderp = @@ -690,11 +690,14 @@ slot_compile_deform(LLVMJitContext *context, TupleDesc desc, int natts) { LLVMValueRef v_off = LLVMBuildLoad(b, v_offp, ""); + LLVMValueRef v_flags; LLVMBuildStore(b, l_int16_const(natts), v_nvalidp); v_off = LLVMBuildTrunc(b, v_off, LLVMInt32Type(), ""); LLVMBuildStore(b, v_off, v_slotoffp); - LLVMBuildStore(b, l_int8_const(1), v_slowp); + v_flags = LLVMBuildLoad(b, v_flagsp, "tts_flags"); + v_flags = LLVMBuildOr(b, v_flags, l_int16_const(TTS_FLAG_SLOW), ""); + LLVMBuildStore(b, v_flags, v_flagsp); LLVMBuildRetVoid(b); } diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c index 99e0cee157..e5fe116acb 100644 --- a/src/backend/jit/llvm/llvmjit_expr.c +++ b/src/backend/jit/llvm/llvmjit_expr.c @@ -292,7 +292,7 @@ llvm_compile_expr(ExprState *state) if (!desc && is && is->ps_ResultTupleSlot && - is->ps_ResultTupleSlot->tts_fixedTupleDescriptor) + TTS_FIXED(is->ps_ResultTupleSlot)) desc = is->ps_ResultTupleSlot->tts_tupleDescriptor; } else if (opcode == EEOP_OUTER_FETCHSOME) @@ -304,7 +304,7 @@ llvm_compile_expr(ExprState *state) if (!desc && os && os->ps_ResultTupleSlot && - os->ps_ResultTupleSlot->tts_fixedTupleDescriptor) + TTS_FIXED(os->ps_ResultTupleSlot)) desc = os->ps_ResultTupleSlot->tts_tupleDescriptor; } else diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index c9ed1988c5..edf538365b 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -340,7 +340,7 @@ ExecProject(ProjectionInfo *projInfo) * Successfully formed a result row. Mark the result slot as containing a * valid virtual tuple (inlined version of ExecStoreVirtualTuple()). */ - slot->tts_isempty = false; + slot->tts_flags &= ~TTS_FLAG_EMPTY; slot->tts_nvalid = slot->tts_tupleDescriptor->natts; return slot; diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index 3c8d57f377..b41b400ef1 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -65,11 +65,11 @@ * ie, only as needed. This serves to avoid repeated extraction of data * from the physical tuple. * - * A TupleTableSlot can also be "empty", holding no valid data. This is - * the only valid state for a freshly-created slot that has not yet had a - * tuple descriptor assigned to it. In this state, tts_isempty must be - * true, tts_shouldFree false, tts_tuple NULL, tts_buffer InvalidBuffer, - * and tts_nvalid zero. + * A TupleTableSlot can also be "empty", indicated by flag TTS_EMPTY set in + * tts_flags, holding no valid data. This is the only valid state for a + * freshly-created slot that has not yet had a tuple descriptor assigned to it. + * In this state, TTS_SHOULDFREE should not be set in tts_flag, tts_tuple must + * be NULL, tts_buffer InvalidBuffer, and tts_nvalid zero. * * The tupleDescriptor is simply referenced, not copied, by the TupleTableSlot * code. The caller of ExecSetSlotDescriptor() is responsible for providing @@ -79,8 +79,9 @@ * mechanism to do more. However, the slot will increment the tupdesc * reference count if a reference-counted tupdesc is supplied.) * - * When tts_shouldFree is true, the physical tuple is "owned" by the slot - * and should be freed when the slot's reference to the tuple is dropped. + * When TTS_SHOULDFREE is set in tts_flags, the physical tuple is "owned" by + * the slot and should be freed when the slot's reference to the tuple is + * dropped. * * If tts_buffer is not InvalidBuffer, then the slot is holding a pin * on the indicated buffer page; drop the pin when we release the @@ -106,35 +107,52 @@ * MINIMAL_TUPLE_OFFSET bytes before tts_mintuple. This allows column * extraction to treat the case identically to regular physical tuples. * - * tts_slow/tts_off are saved state for slot_deform_tuple, and should not - * be touched by any other code. + * TTS_SLOW flag in tts_flags and tts_off are saved state for + * slot_deform_tuple, and should not be touched by any other code. *---------- */ + +/* true = slot is empty */ +#define TTS_FLAG_EMPTY (1 << 1) +#define TTS_EMPTY(slot) (((slot)->tts_flags & TTS_FLAG_EMPTY) != 0) + +/* should pfree tts_tuple? */ +#define TTS_FLAG_SHOULDFREE (1 << 2) +#define TTS_SHOULDFREE(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREE) != 0) + +/* should pfree tts_mintuple? */ +#define TTS_FLAG_SHOULDFREEMIN (1 << 3) +#define TTS_SHOULDFREEMIN(slot) (((slot)->tts_flags & TTS_FLAG_SHOULDFREEMIN) != 0) + +/* saved state for slot_deform_tuple */ +#define TTS_FLAG_SLOW (1 << 4) +#define TTS_SLOW(slot) (((slot)->tts_flags & TTS_FLAG_SLOW) != 0) + +/* fixed tuple descriptor */ +#define TTS_FLAG_FIXED (1 << 5) +#define TTS_FIXED(slot) (((slot)->tts_flags & TTS_FLAG_FIXED) != 0) + typedef struct TupleTableSlot { NodeTag type; - bool tts_isempty; /* true = slot is empty */ - bool tts_shouldFree; /* should pfree tts_tuple? */ - bool tts_shouldFreeMin; /* should pfree tts_mintuple? */ -#define FIELDNO_TUPLETABLESLOT_SLOW 4 - bool tts_slow; /* saved state for slot_deform_tuple */ -#define FIELDNO_TUPLETABLESLOT_TUPLE 5 +#define FIELDNO_TUPLETABLESLOT_FLAGS 1 + uint16 tts_flags; /* Boolean states */ +#define FIELDNO_TUPLETABLESLOT_NVALID 2 + AttrNumber tts_nvalid; /* # of valid values in tts_values */ +#define FIELDNO_TUPLETABLESLOT_TUPLE 3 HeapTuple tts_tuple; /* physical tuple, or NULL if virtual */ -#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 6 +#define FIELDNO_TUPLETABLESLOT_TUPLEDESCRIPTOR 4 TupleDesc tts_tupleDescriptor; /* slot's tuple descriptor */ MemoryContext tts_mcxt; /* slot itself is in this context */ Buffer tts_buffer; /* tuple's buffer, or InvalidBuffer */ -#define FIELDNO_TUPLETABLESLOT_NVALID 9 - AttrNumber tts_nvalid; /* # of valid values in tts_values */ -#define FIELDNO_TUPLETABLESLOT_VALUES 10 +#define FIELDNO_TUPLETABLESLOT_OFF 7 + uint32 tts_off; /* saved state for slot_deform_tuple */ +#define FIELDNO_TUPLETABLESLOT_VALUES 8 Datum *tts_values; /* current per-attribute values */ -#define FIELDNO_TUPLETABLESLOT_ISNULL 11 +#define FIELDNO_TUPLETABLESLOT_ISNULL 9 bool *tts_isnull; /* current per-attribute isnull flags */ MinimalTuple tts_mintuple; /* minimal tuple, or NULL if none */ HeapTupleData tts_minhdr; /* workspace for minimal-tuple-only case */ -#define FIELDNO_TUPLETABLESLOT_OFF 14 - uint32 tts_off; /* saved state for slot_deform_tuple */ - bool tts_fixedTupleDescriptor; /* descriptor can't be changed */ } TupleTableSlot; #define TTS_HAS_PHYSICAL_TUPLE(slot) \ @@ -144,7 +162,7 @@ typedef struct TupleTableSlot * TupIsNull -- is a TupleTableSlot empty? */ #define TupIsNull(slot) \ - ((slot) == NULL || (slot)->tts_isempty) + ((slot) == NULL || TTS_EMPTY(slot)) /* in executor/execTuples.c */ extern TupleTableSlot *MakeTupleTableSlot(TupleDesc desc);