public inbox for [email protected]
help / color / mirror / Atom feedRe: Shared detoast Datum proposal
11+ messages / 6 participants
[nested] [flat]
* Re: Shared detoast Datum proposal
@ 2024-01-01 13:55 Andy Fan <[email protected]>
2024-01-06 15:12 ` Re: Shared detoast Datum proposal vignesh C <[email protected]>
0 siblings, 1 reply; 11+ messages in thread
From: Andy Fan @ 2024-01-01 13:55 UTC (permalink / raw)
To: Andy Fan <[email protected]>; +Cc: [email protected] <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>
Andy Fan <[email protected]> writes:
>
> Some Known issues:
> ------------------
>
> 1. Currently only Scan & Join nodes are considered for this feature.
> 2. JIT is not adapted for this purpose yet.
JIT is adapted for this feature in v2. Any feedback is welcome.
--
Best Regards
Andy Fan
Attachments:
[text/x-diff] v2-0001-shared-detoast-feature.patch (74.9K, ../../[email protected]/2-v2-0001-shared-detoast-feature.patch)
download | inline diff:
From ee44d4721147589dbba8366382a18adeee05419b Mon Sep 17 00:00:00 2001
From: "yizhi.fzh" <[email protected]>
Date: Wed, 27 Dec 2023 18:43:56 +0800
Subject: [PATCH v2 1/1] shared detoast feature.
---
src/backend/executor/execExpr.c | 65 ++-
src/backend/executor/execExprInterp.c | 181 +++++++
src/backend/executor/execTuples.c | 130 +++++
src/backend/executor/execUtils.c | 5 +
src/backend/executor/nodeHashjoin.c | 2 +
src/backend/executor/nodeMergejoin.c | 2 +
src/backend/executor/nodeNestloop.c | 1 +
src/backend/jit/llvm/llvmjit_expr.c | 22 +
src/backend/jit/llvm/llvmjit_types.c | 1 +
src/backend/nodes/bitmapset.c | 13 +
src/backend/optimizer/plan/createplan.c | 73 ++-
src/backend/optimizer/plan/setrefs.c | 536 +++++++++++++++----
src/include/executor/execExpr.h | 6 +
src/include/executor/tuptable.h | 60 +++
src/include/nodes/bitmapset.h | 1 +
src/include/nodes/execnodes.h | 5 +
src/include/nodes/plannodes.h | 50 ++
src/test/regress/sql/shared_detoast_slow.sql | 70 +++
18 files changed, 1117 insertions(+), 106 deletions(-)
create mode 100644 src/test/regress/sql/shared_detoast_slow.sql
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 2c62b0c9c8..749bcc9023 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -935,22 +935,81 @@ ExecInitExprRec(Expr *node, ExprState *state,
}
else
{
+ int attnum;
+ Plan *plan = state->parent ? state->parent->plan : NULL;
+
/* regular user column */
scratch.d.var.attnum = variable->varattno - 1;
scratch.d.var.vartype = variable->vartype;
+ attnum = scratch.d.var.attnum;
+
switch (variable->varno)
{
case INNER_VAR:
- scratch.opcode = EEOP_INNER_VAR;
+
+ if (is_join_plan(plan) &&
+ bms_is_member(attnum,
+ ((JoinState *) state->parent)->inner_pre_detoast_attrs))
+ {
+ /* debug purpose. */
+ if (!jit_enabled)
+ {
+ elog(INFO,
+ "EEOP_INNER_VAR_TOAST: flags = %d costs=%.2f..%.2f, attnum: %d",
+ state->flags,
+ plan->startup_cost,
+ plan->total_cost,
+ attnum);
+ }
+ scratch.opcode = EEOP_INNER_VAR_TOAST;
+ }
+ else
+ {
+ scratch.opcode = EEOP_INNER_VAR;
+ }
break;
case OUTER_VAR:
- scratch.opcode = EEOP_OUTER_VAR;
+ if (is_join_plan(plan) &&
+ bms_is_member(attnum,
+ ((JoinState *) state->parent)->outer_pre_detoast_attrs))
+ {
+ /* debug purpose. */
+ if (!jit_enabled)
+ {
+ elog(INFO,
+ "EEOP_OUTER_VAR_TOAST: flags = %u costs=%.2f..%.2f, attnum: %d",
+ state->flags,
+ plan->startup_cost,
+ plan->total_cost,
+ attnum);
+ }
+ scratch.opcode = EEOP_OUTER_VAR_TOAST;
+ }
+ else
+ scratch.opcode = EEOP_OUTER_VAR;
break;
/* INDEX_VAR is handled by default case */
default:
- scratch.opcode = EEOP_SCAN_VAR;
+ if (is_scan_plan(plan) && bms_is_member(
+ attnum,
+ ((ScanState *) state->parent)->scan_pre_detoast_attrs))
+ {
+ if (!jit_enabled)
+ {
+ elog(INFO,
+ "EEOP_SCAN_VAR_TOAST: flags = %u costs=%.2f..%.2f, scanId: %d, attnum: %d",
+ state->flags,
+ plan->startup_cost,
+ plan->total_cost,
+ ((Scan *) plan)->scanrelid,
+ attnum);
+ }
+ scratch.opcode = EEOP_SCAN_VAR_TOAST;
+ }
+ else
+ scratch.opcode = EEOP_SCAN_VAR;
break;
}
}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 24c2b60c62..b5a464bf80 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -57,6 +57,7 @@
#include "postgres.h"
#include "access/heaptoast.h"
+#include "access/detoast.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
#include "executor/execExpr.h"
@@ -157,6 +158,9 @@ static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
@@ -165,6 +169,9 @@ static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull
static Datum ExecJustInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
@@ -180,6 +187,43 @@ static pg_attribute_always_inline void ExecAggPlainTransByRef(AggState *aggstate
AggStatePerGroup pergroup,
ExprContext *aggcontext,
int setno);
+static inline void
+ExecSlotDetoastDatum(TupleTableSlot *slot, int attnum)
+{
+ if (!slot->tts_isnull[attnum] &&
+ VARATT_IS_EXTENDED(slot->tts_values[attnum]))
+ {
+ Datum oldDatum;
+ MemoryContext old = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ oldDatum = slot->tts_values[attnum];
+ slot->tts_values[attnum] = PointerGetDatum(detoast_attr(
+ (struct varlena *) oldDatum));
+ Assert(slot->tts_nvalid > attnum);
+ if (oldDatum != slot->tts_values[attnum])
+ slot->pre_detoasted_attrs = bms_add_member(slot->pre_detoasted_attrs, attnum);
+ MemoryContextSwitchTo(old);
+ }
+}
+
+/* JIT requires a non-static (and external?) function */
+void
+ExecSlotDetoastDatumExternal(TupleTableSlot *slot, int attnum)
+{
+ return ExecSlotDetoastDatum(slot, attnum);
+}
+
+
+static inline void
+ExecEvalToastVar(TupleTableSlot *slot,
+ ExprEvalStep *op,
+ int attnum)
+{
+ ExecSlotDetoastDatum(slot, attnum);
+
+ *op->resvalue = slot->tts_values[attnum];
+ *op->resnull = slot->tts_isnull[attnum];
+}
/*
* ScalarArrayOpExprHashEntry
@@ -295,6 +339,24 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustScanVar;
return;
}
+ if (step0 == EEOP_INNER_FETCHSOME &&
+ step1 == EEOP_INNER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustInnerVarToast;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_FETCHSOME &&
+ step1 == EEOP_OUTER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustOuterVarToast;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_FETCHSOME &&
+ step1 == EEOP_SCAN_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustScanVarToast;
+ return;
+ }
else if (step0 == EEOP_INNER_FETCHSOME &&
step1 == EEOP_ASSIGN_INNER_VAR)
{
@@ -330,6 +392,7 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustConst;
return;
}
+ /* ???? */
else if (step0 == EEOP_INNER_VAR)
{
state->evalfunc_private = (void *) ExecJustInnerVarVirt;
@@ -345,6 +408,21 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustScanVarVirt;
return;
}
+ else if (step0 == EEOP_INNER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustInnerVarVirtToast;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustOuterVarVirtToast;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustScanVarVirtToast;
+ return;
+ }
else if (step0 == EEOP_ASSIGN_INNER_VAR)
{
state->evalfunc_private = (void *) ExecJustAssignInnerVarVirt;
@@ -412,6 +490,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_INNER_VAR,
&&CASE_EEOP_OUTER_VAR,
&&CASE_EEOP_SCAN_VAR,
+ &&CASE_EEOP_INNER_VAR_TOAST,
+ &&CASE_EEOP_OUTER_VAR_TOAST,
+ &&CASE_EEOP_SCAN_VAR_TOAST,
&&CASE_EEOP_INNER_SYSVAR,
&&CASE_EEOP_OUTER_SYSVAR,
&&CASE_EEOP_SCAN_SYSVAR,
@@ -595,6 +676,25 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
*op->resvalue = scanslot->tts_values[attnum];
*op->resnull = scanslot->tts_isnull[attnum];
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_INNER_VAR_TOAST)
+ {
+ ExecEvalToastVar(innerslot, op, op->d.var.attnum);
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_OUTER_VAR_TOAST)
+ {
+ ExecEvalToastVar(outerslot, op, op->d.var.attnum);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SCAN_VAR_TOAST)
+ {
+ ExecEvalToastVar(scanslot, op, op->d.var.attnum);
EEO_NEXT();
}
@@ -2126,6 +2226,42 @@ ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
return ExecJustVarImpl(state, econtext->ecxt_scantuple, isnull);
}
+static pg_attribute_always_inline Datum
+ExecJustVarImplToast(ExprState *state, TupleTableSlot *slot, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum;
+
+ CheckOpSlotCompatibility(&state->steps[0], slot);
+
+ slot_getattr(slot, attnum + 1, isnull);
+
+ ExecSlotDetoastDatum(slot, attnum);
+
+ return slot->tts_values[attnum];
+}
+
+/* Simple reference to inner Var */
+static Datum
+ExecJustInnerVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_innertuple, isnull);
+}
+
+/* Simple reference to outer Var */
+static Datum
+ExecJustOuterVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_outertuple, isnull);
+}
+
+/* Simple reference to scan Var */
+static Datum
+ExecJustScanVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_scantuple, isnull);
+}
+
/* implementation of ExecJustAssign(Inner|Outer|Scan)Var */
static pg_attribute_always_inline Datum
ExecJustAssignVarImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
@@ -2264,6 +2400,51 @@ ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
return ExecJustVarVirtImpl(state, econtext->ecxt_scantuple, isnull);
}
+/* implementation of ExecJust(Inner|Outer|Scan)VarVirt */
+static pg_attribute_always_inline Datum
+ExecJustVarVirtImplToast(ExprState *state, TupleTableSlot *slot, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[0];
+ int attnum = op->d.var.attnum;
+
+ /*
+ * As it is guaranteed that a virtual slot is used, there never is a need
+ * to perform tuple deforming (nor would it be possible). Therefore
+ * execExpr.c has not emitted an EEOP_*_FETCHSOME step. Verify, as much as
+ * possible, that that determination was accurate.
+ */
+ Assert(TTS_IS_VIRTUAL(slot));
+ Assert(TTS_FIXED(slot));
+ Assert(attnum >= 0 && attnum < slot->tts_nvalid);
+
+ *isnull = slot->tts_isnull[attnum];
+
+ ExecSlotDetoastDatum(slot, attnum);
+
+ return slot->tts_values[attnum];
+}
+
+/* Like ExecJustInnerVar, optimized for virtual slots */
+static Datum
+ExecJustInnerVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_innertuple, isnull);
+}
+
+/* Like ExecJustOuterVar, optimized for virtual slots */
+static Datum
+ExecJustOuterVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_outertuple, isnull);
+}
+
+/* Like ExecJustScanVar, optimized for virtual slots */
+static Datum
+ExecJustScanVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_scantuple, isnull);
+}
+
/* implementation of ExecJustAssign(Inner|Outer|Scan)VarVirt */
static pg_attribute_always_inline Datum
ExecJustAssignVarVirtImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 2c2712ceac..68cc3a2f2e 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -79,6 +79,9 @@ static inline void tts_buffer_heap_store_tuple(TupleTableSlot *slot,
bool transfer_pin);
static void tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree);
+static Bitmapset *cal_final_pre_detoast_attrs(Bitmapset *plan_pre_detoast_attrs,
+ TupleDesc tupleDesc,
+ List *not_pre_detoast_vars);
const TupleTableSlotOps TTSOpsVirtual;
const TupleTableSlotOps TTSOpsHeapTuple;
@@ -176,6 +179,10 @@ tts_virtual_materialize(TupleTableSlot *slot)
if (att->attbyval || slot->tts_isnull[natt])
continue;
+ if (bms_is_member(natt, slot->pre_detoasted_attrs))
+ /* it has been in slot->tts_mcxt already. */
+ continue;
+
val = slot->tts_values[natt];
if (att->attlen == -1 &&
@@ -394,6 +401,13 @@ tts_heap_materialize(TupleTableSlot *slot)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_values is treated as non valid (tts_nvalid = 0), so let free the
+ * pre-detoast datum.
+ */
+ ExecFreePreDetoastDatum(slot);
+
}
static void
@@ -459,6 +473,9 @@ tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /* slot_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
@@ -569,6 +586,12 @@ tts_minimal_materialize(TupleTableSlot *slot)
mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_values is treated as non valid (tts_nvalid = 0), free the
+ * pre-detoast datum.
+ */
+ ExecFreePreDetoastDatum(slot);
}
static void
@@ -639,6 +662,9 @@ tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /* tts_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
@@ -773,6 +799,12 @@ tts_buffer_heap_materialize(TupleTableSlot *slot)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_nvalid = 0 means tts_values will be not reliable, so clear the
+ * information about pre-detoast-datum.
+ */
+ ExecFreePreDetoastDatum(slot);
}
static void
@@ -862,6 +894,7 @@ tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
if (TTS_SHOULDFREE(slot))
{
/* materialized slot shouldn't have a buffer to release */
@@ -906,6 +939,8 @@ tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
*/
ReleaseBuffer(buffer);
}
+
+ ExecFreePreDetoastDatum(slot);
}
/*
@@ -1140,6 +1175,7 @@ MakeTupleTableSlot(TupleDesc tupleDesc,
slot->tts_tupleDescriptor = tupleDesc;
slot->tts_mcxt = CurrentMemoryContext;
slot->tts_nvalid = 0;
+ slot->pre_detoasted_attrs = NULL;
if (tupleDesc != NULL)
{
@@ -1812,12 +1848,30 @@ void
ExecInitScanTupleSlot(EState *estate, ScanState *scanstate,
TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
{
+ Scan *splan = (Scan *) scanstate->ps.plan;
+
scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
tupledesc, tts_ops);
scanstate->ps.scandesc = tupledesc;
scanstate->ps.scanopsfixed = tupledesc != NULL;
scanstate->ps.scanops = tts_ops;
scanstate->ps.scanopsset = true;
+
+ if (is_scan_plan((Plan *) splan))
+ {
+ /*
+ * We may run detoast in Qual or Projection, but all of them happen at
+ * the ss_ScanTupleSlot rather than ps_ResultTupleSlot. So we can only
+ * take care of the ss_ScanTupleSlot.
+ *
+ * the ps_ResultTupleSlot may also have detoast on its parent node,
+ * like as a inner or outer slot in join case, the pre_detoast on
+ * these slot.
+ */
+ scanstate->scan_pre_detoast_attrs = cal_final_pre_detoast_attrs(splan->reference_attrs,
+ tupledesc,
+ splan->plan.forbid_pre_detoast_vars);
+ }
}
/* ----------------
@@ -2338,3 +2392,79 @@ end_tup_output(TupOutputState *tstate)
ExecDropSingleTupleTableSlot(tstate->slot);
pfree(tstate);
}
+
+static Bitmapset *
+cal_final_pre_detoast_attrs(Bitmapset *plan_pre_detoast_attrs,
+ TupleDesc tupleDesc,
+ List *not_pre_detoast_vars)
+{
+ Bitmapset *final = NULL,
+ *toast_attrs = NULL,
+ *forbid_pre_detoast_attrs = NULL;
+
+ int i;
+ ListCell *lc;
+
+ if (bms_is_empty(plan_pre_detoast_attrs))
+ return NULL;
+
+ /*
+ * there is no exact data type in create_plan or set_plan_refs stage, so
+ * plan_pre_detoast_attrs may have some attribute which is not toast attrs
+ * at all, which should be removed.
+ */
+ for (i = 0; i < tupleDesc->natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(tupleDesc, i);
+
+ if (attr->attlen == -1 && attr->attstorage != TYPSTORAGE_PLAIN)
+ toast_attrs = bms_add_member(toast_attrs, attr->attnum - 1);
+ }
+
+ final = bms_intersect(plan_pre_detoast_attrs, toast_attrs);
+
+ /*
+ * Due to the fact of detoast-datum will make the tuple bigger which is
+ * bad for some nodes like Sort/Hash, to avoid performance regression,
+ * such attribute should be removed as well.
+ */
+ foreach(lc, not_pre_detoast_vars)
+ {
+ Var *var = lfirst_node(Var, lc);
+
+ forbid_pre_detoast_attrs = bms_add_member(forbid_pre_detoast_attrs, var->varattno - 1);
+ }
+
+ final = bms_del_members(final, forbid_pre_detoast_attrs);
+
+ bms_free(toast_attrs);
+ bms_free(forbid_pre_detoast_attrs);
+
+ return final;
+}
+
+
+/* Input slot, result slot. scan slot? */
+void
+SetPredetoastAttrsForScan(ScanState *scanstate)
+{
+}
+
+void
+SetPredetoastAttrsForJoin(JoinState *j)
+{
+ PlanState *outerstate = outerPlanState(j);
+ PlanState *innerstate = innerPlanState(j);
+
+ /* Input slot, result slot. scan slot? */
+
+ j->outer_pre_detoast_attrs = cal_final_pre_detoast_attrs(
+ ((Join *) j->ps.plan)->outer_reference_attrs,
+ outerstate->ps_ResultTupleDesc,
+ outerstate->plan->forbid_pre_detoast_vars);
+
+ j->inner_pre_detoast_attrs = cal_final_pre_detoast_attrs(
+ ((Join *) j->ps.plan)->inner_reference_attrs,
+ innerstate->ps_ResultTupleDesc,
+ innerstate->plan->forbid_pre_detoast_vars);
+}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 16704c0c2f..3817979b37 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -572,6 +572,11 @@ ExecConditionalAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc,
planstate->resultopsset = planstate->scanopsset;
planstate->resultopsfixed = planstate->scanopsfixed;
planstate->resultops = planstate->scanops;
+
+ /*
+ * XXX: can I make sure the ps_ResultTupleDesc is set all the time?
+ */
+ Assert(planstate->ps_ResultTupleDesc != NULL);
}
else
{
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 25a2d78f15..e3801d3b20 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -756,6 +756,8 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate, eflags);
innerDesc = ExecGetResultType(innerPlanState(hjstate));
+ SetPredetoastAttrsForJoin((JoinState *) hjstate);
+
/*
* Initialize result slot, type and projection.
*/
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 3cdab77dfc..648ab6903d 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -1497,6 +1497,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
(eflags | EXEC_FLAG_MARK));
innerDesc = ExecGetResultType(innerPlanState(mergestate));
+ SetPredetoastAttrsForJoin((JoinState *) mergestate);
+
/*
* For certain types of inner child nodes, it is advantageous to issue
* MARK every time we advance past an inner tuple we will never return to.
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index ebd1406843..c869a516ec 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -306,6 +306,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
*/
ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
+ SetPredetoastAttrsForJoin((JoinState *) nlstate);
/*
* initialize child expressions
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index a3a0876bff..2fcc8dad36 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -396,30 +396,52 @@ llvm_compile_expr(ExprState *state)
case EEOP_INNER_VAR:
case EEOP_OUTER_VAR:
case EEOP_SCAN_VAR:
+ case EEOP_INNER_VAR_TOAST:
+ case EEOP_OUTER_VAR_TOAST:
+ case EEOP_SCAN_VAR_TOAST:
{
LLVMValueRef value,
isnull;
LLVMValueRef v_attnum;
LLVMValueRef v_values;
LLVMValueRef v_nulls;
+ LLVMValueRef v_slot;
if (opcode == EEOP_INNER_VAR)
{
+ v_slot = v_innerslot;
v_values = v_innervalues;
v_nulls = v_innernulls;
}
else if (opcode == EEOP_OUTER_VAR)
{
+ v_slot = v_outerslot;
v_values = v_outervalues;
v_nulls = v_outernulls;
}
else
{
+ v_slot = v_scanslot;
v_values = v_scanvalues;
v_nulls = v_scannulls;
}
v_attnum = l_int32_const(lc, op->d.var.attnum);
+
+ if (opcode == EEOP_INNER_VAR_TOAST ||
+ opcode == EEOP_OUTER_VAR_TOAST ||
+ opcode == EEOP_SCAN_VAR_TOAST)
+ {
+ LLVMValueRef params[2];
+
+ params[0] = v_slot;
+ params[1] = l_int32_const(lc, op->d.var.attnum);
+ l_call(b,
+ llvm_pg_var_func_type("ExecSlotDetoastDatumExternal"),
+ llvm_pg_func(mod, "ExecSlotDetoastDatumExternal"),
+ params, lengthof(params), "");
+ }
+
value = l_load_gep1(b, TypeSizeT, v_values, v_attnum, "");
isnull = l_load_gep1(b, TypeStorageBool, v_nulls, v_attnum, "");
LLVMBuildStore(b, value, v_resvaluep);
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 791902ff1f..7aee794100 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -177,4 +177,5 @@ void *referenced_functions[] =
strlen,
varsize_any,
ExecInterpExprStillValid,
+ ExecSlotDetoastDatumExternal,
};
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 704879f566..d81f8afc33 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -743,6 +743,19 @@ bms_membership(const Bitmapset *a)
* foo = bms_add_member(foo, x);
*/
+/*
+ * does this break commit 00b41463c21615f9bf3927f207e37f9e215d32e6?
+ * but I just found alloc memory and free the memory is too bad
+ * for this current feature. So let see ...;
+ */
+void
+bms_zero(Bitmapset *a)
+{
+ if (a == NULL)
+ return;
+
+ memset(a->words, 0, a->nwords * sizeof(bitmapword));
+}
/*
* bms_add_member - add a specified member to set
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 34ca6d4ac2..4d0e1b8eb1 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -42,6 +42,7 @@
#include "partitioning/partprune.h"
#include "utils/lsyscache.h"
+extern bool jit_enabled;
/*
* Flag bits that can appear in the flags argument of create_plan_recurse().
@@ -314,7 +315,8 @@ static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan,
List *mergeActionLists, int epqParam);
static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
GatherMergePath *best_path);
-
+static void set_plan_not_detoast_attrs_recurse(Plan *plan,
+ List *recheck_list);
/*
* create_plan
@@ -346,6 +348,8 @@ create_plan(PlannerInfo *root, Path *best_path)
/* Recursively process the path tree, demanding the correct tlist result */
plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST);
+ set_plan_not_detoast_attrs_recurse(plan, NIL);
+
/*
* Make sure the topmost plan node's targetlist exposes the original
* column names and other decorative info. Targetlists generated within
@@ -378,6 +382,68 @@ create_plan(PlannerInfo *root, Path *best_path)
return plan;
}
+/*
+ * set_plan_not_pre_detoast_vars
+ *
+ * set the toast_attrs according recheck_list.
+ *
+ * recheck_list = NIL means we need to do thing.
+ */
+static void
+set_plan_not_pre_detoast_vars(Plan *plan, List *recheck_list)
+{
+ ListCell *lc;
+ Var *var;
+
+ if (recheck_list == NIL)
+ return;
+
+ foreach(lc, plan->targetlist)
+ {
+ TargetEntry *te = lfirst_node(TargetEntry, lc);
+
+ if (!IsA(te->expr, Var))
+ continue;
+ var = castNode(Var, te->expr);
+ if (var->varattno <= 0)
+ continue;
+ if (list_member(recheck_list, var))
+ /* pass the recheck */
+ plan->forbid_pre_detoast_vars = lappend(plan->forbid_pre_detoast_vars, var);
+ }
+}
+
+
+static void
+set_plan_not_detoast_attrs_recurse(Plan *plan, List *recheck_list)
+{
+ if (plan == NULL)
+ return;
+
+ set_plan_not_pre_detoast_vars(plan, recheck_list);
+
+ if (IsA(plan, Sort) || IsA(plan, Memoize) || IsA(plan, WindowAgg) ||
+ IsA(plan, Hash) || IsA(plan, Material) || IsA(plan, IncrementalSort))
+ {
+ List *subplan_exprs = get_tlist_exprs(plan->lefttree->targetlist, true);
+
+ set_plan_not_pre_detoast_vars(plan, subplan_exprs);
+ set_plan_not_detoast_attrs_recurse(plan->lefttree, subplan_exprs);
+ }
+ else if (IsA(plan, HashJoin) && castNode(HashJoin, plan)->left_small_tlist)
+ {
+ List *subplan_exprs = get_tlist_exprs(plan->lefttree->targetlist, true);
+
+ set_plan_not_detoast_attrs_recurse(plan->lefttree, subplan_exprs);
+ set_plan_not_detoast_attrs_recurse(plan->righttree, plan->forbid_pre_detoast_vars);
+ }
+ else
+ {
+ set_plan_not_detoast_attrs_recurse(plan->lefttree, plan->forbid_pre_detoast_vars);
+ set_plan_not_detoast_attrs_recurse(plan->righttree, plan->forbid_pre_detoast_vars);
+ }
+}
+
/*
* create_plan_recurse
* Recursive guts of create_plan().
@@ -4884,6 +4950,11 @@ create_hashjoin_plan(PlannerInfo *root,
copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
+ join_plan->left_small_tlist = (best_path->num_batches > 1);
+
+ if (!jit_enabled)
+ elog(INFO, "num_batches = %d", best_path->num_batches);
+
return join_plan;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 4bb68ac90e..df8da328a0 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -27,6 +27,7 @@
#include "optimizer/tlist.h"
#include "parser/parse_relation.h"
#include "tcop/utility.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -55,11 +56,27 @@ typedef struct
tlist_vinfo vars[FLEXIBLE_ARRAY_MEMBER]; /* has num_vars entries */
} indexed_tlist;
+typedef struct
+{
+ /* var is added into existing_attrs for the first time. */
+ Bitmapset *existing_attrs;
+ /* following add to the final_ref_attrs. */
+ Bitmapset **final_ref_attrs;
+} intermediate_var_ref_context;
+
+typedef struct
+{
+ int level_added;
+ int level;
+} intermediate_level_context;
+
typedef struct
{
PlannerInfo *root;
int rtoffset;
double num_exec;
+ intermediate_level_context level_ctx;
+ intermediate_var_ref_context scan_reference_attrs;
} fix_scan_expr_context;
typedef struct
@@ -71,6 +88,9 @@ typedef struct
int rtoffset;
NullingRelsMatch nrm_match;
double num_exec;
+ intermediate_level_context level_ctx;
+ intermediate_var_ref_context outer_reference_attrs;
+ intermediate_var_ref_context inner_reference_attrs;
} fix_join_expr_context;
typedef struct
@@ -127,8 +147,8 @@ typedef struct
(((con)->consttype == REGCLASSOID || (con)->consttype == OIDOID) && \
!(con)->constisnull)
-#define fix_scan_list(root, lst, rtoffset, num_exec) \
- ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec))
+#define fix_scan_list(root, lst, rtoffset, num_exec, pre_detoast_attrs) \
+ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec, pre_detoast_attrs))
static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing);
static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
@@ -158,7 +178,8 @@ static Plan *set_mergeappend_references(PlannerInfo *root,
static void set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset);
static Relids offset_relid_set(Relids relids, int rtoffset);
static Node *fix_scan_expr(PlannerInfo *root, Node *node,
- int rtoffset, double num_exec);
+ int rtoffset, double num_exec,
+ Bitmapset **scan_reference_attrs);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
@@ -190,7 +211,10 @@ static List *fix_join_expr(PlannerInfo *root,
Index acceptable_rel,
int rtoffset,
NullingRelsMatch nrm_match,
- double num_exec);
+ double num_exec,
+ Bitmapset **outer_reference_attrs,
+ Bitmapset **inner_reference_attrs);
+
static Node *fix_join_expr_mutator(Node *node,
fix_join_expr_context *context);
static Node *fix_upper_expr(PlannerInfo *root,
@@ -628,10 +652,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_SampleScan:
@@ -641,13 +671,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs
+ );
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tablesample = (TableSampleClause *)
fix_scan_expr(root, (Node *) splan->tablesample,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_IndexScan:
@@ -657,28 +694,40 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
+
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
splan->indexqual =
fix_scan_list(root, splan->indexqual,
- rtoffset, 1);
+ rtoffset, 1, &splan->scan.reference_attrs);
splan->indexqualorig =
fix_scan_list(root, splan->indexqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->indexorderby =
fix_scan_list(root, splan->indexorderby,
- rtoffset, 1);
+ rtoffset, 1, &splan->scan.reference_attrs);
splan->indexorderbyorig =
fix_scan_list(root, splan->indexorderbyorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan), &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_IndexOnlyScan:
{
IndexOnlyScan *splan = (IndexOnlyScan *) plan;
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
+
return set_indexonlyscan_references(root, splan, rtoffset);
}
break;
@@ -691,10 +740,15 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->scan.plan.targetlist == NIL);
Assert(splan->scan.plan.qual == NIL);
splan->indexqual =
- fix_scan_list(root, splan->indexqual, rtoffset, 1);
+ fix_scan_list(root, splan->indexqual, rtoffset, 1,
+ &splan->scan.reference_attrs);
splan->indexqualorig =
fix_scan_list(root, splan->indexqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_BitmapHeapScan:
@@ -704,13 +758,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->bitmapqualorig =
fix_scan_list(root, splan->bitmapqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_TidScan:
@@ -720,13 +781,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tidquals =
fix_scan_list(root, splan->tidquals,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_TidRangeScan:
@@ -736,17 +804,25 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tidrangequals =
fix_scan_list(root, splan->tidrangequals,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_SubqueryScan:
/* Needs special treatment, see comments below */
+ /* XXX: shall I do anything? */
return set_subqueryscan_references(root,
(SubqueryScan *) plan,
rtoffset);
@@ -757,12 +833,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->functions =
- fix_scan_list(root, splan->functions, rtoffset, 1);
+ fix_scan_list(root, splan->functions, rtoffset, 1,
+ &splan->scan.reference_attrs);
+
}
break;
case T_TableFuncScan:
@@ -772,13 +852,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
splan->tablefunc = (TableFunc *)
fix_scan_expr(root, (Node *) splan->tablefunc,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
}
break;
case T_ValuesScan:
@@ -788,13 +872,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->values_lists =
fix_scan_list(root, splan->values_lists,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
}
break;
case T_CteScan:
@@ -804,10 +891,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_NamedTuplestoreScan:
@@ -817,10 +910,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
}
break;
case T_WorkTableScan:
@@ -830,10 +925,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
}
break;
case T_ForeignScan:
@@ -873,7 +970,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
mplan->param_exprs = fix_scan_list(root, mplan->param_exprs,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan),
+ NULL);
break;
}
@@ -933,9 +1031,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->plan.qual == NIL);
splan->limitOffset =
- fix_scan_expr(root, splan->limitOffset, rtoffset, 1);
+ fix_scan_expr(root, splan->limitOffset, rtoffset, 1, NULL);
splan->limitCount =
- fix_scan_expr(root, splan->limitCount, rtoffset, 1);
+ fix_scan_expr(root, splan->limitCount, rtoffset, 1, NULL);
}
break;
case T_Agg:
@@ -988,17 +1086,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
* variable refs, so fix_scan_expr works for them.
*/
wplan->startOffset =
- fix_scan_expr(root, wplan->startOffset, rtoffset, 1);
+ fix_scan_expr(root, wplan->startOffset, rtoffset, 1, NULL);
wplan->endOffset =
- fix_scan_expr(root, wplan->endOffset, rtoffset, 1);
+ fix_scan_expr(root, wplan->endOffset, rtoffset, 1, NULL);
wplan->runCondition = fix_scan_list(root,
wplan->runCondition,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan), NULL);
wplan->runConditionOrig = fix_scan_list(root,
wplan->runConditionOrig,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_Result:
@@ -1038,14 +1136,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->plan.targetlist =
fix_scan_list(root, splan->plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
splan->plan.qual =
fix_scan_list(root, splan->plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan), NULL);
}
/* resconstantqual can't contain any subplan variable refs */
splan->resconstantqual =
- fix_scan_expr(root, splan->resconstantqual, rtoffset, 1);
+ fix_scan_expr(root, splan->resconstantqual, rtoffset, 1, NULL);
}
break;
case T_ProjectSet:
@@ -1061,7 +1159,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->withCheckOptionLists =
fix_scan_list(root, splan->withCheckOptionLists,
- rtoffset, 1);
+ rtoffset, 1, NULL);
if (splan->returningLists)
{
@@ -1118,18 +1216,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
fix_join_expr(root, splan->onConflictSet,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan),
+ NULL, NULL);
splan->onConflictWhere = (Node *)
fix_join_expr(root, (List *) splan->onConflictWhere,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan),
+ NULL, NULL);
pfree(itlist);
splan->exclRelTlist =
- fix_scan_list(root, splan->exclRelTlist, rtoffset, 1);
+ fix_scan_list(root, splan->exclRelTlist, rtoffset, 1, NULL);
}
/*
@@ -1182,7 +1282,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
resultrel,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan),
+ NULL, NULL);
/* Fix quals too. */
action->qual = (Node *) fix_join_expr(root,
@@ -1191,7 +1292,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
resultrel,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL(plan));
+ NUM_EXEC_QUAL(plan),
+ NULL, NULL);
}
}
}
@@ -1356,13 +1458,16 @@ set_indexonlyscan_references(PlannerInfo *root,
NUM_EXEC_QUAL((Plan *) plan));
/* indexqual is already transformed to reference index columns */
plan->indexqual = fix_scan_list(root, plan->indexqual,
- rtoffset, 1);
+ rtoffset, 1,
+ &plan->scan.reference_attrs);
/* indexorderby is already transformed to reference index columns */
plan->indexorderby = fix_scan_list(root, plan->indexorderby,
- rtoffset, 1);
+ rtoffset, 1,
+ &plan->scan.reference_attrs);
/* indextlist must NOT be transformed to reference index columns */
plan->indextlist = fix_scan_list(root, plan->indextlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) plan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan),
+ &plan->scan.reference_attrs);
pfree(index_itlist);
@@ -1409,10 +1514,10 @@ set_subqueryscan_references(PlannerInfo *root,
plan->scan.scanrelid += rtoffset;
plan->scan.plan.targetlist =
fix_scan_list(root, plan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) plan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan), NULL);
plan->scan.plan.qual =
fix_scan_list(root, plan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) plan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) plan), NULL);
result = (Plan *) plan;
}
@@ -1612,7 +1717,7 @@ set_foreignscan_references(PlannerInfo *root,
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
fscan->fdw_scan_tlist =
fix_scan_list(root, fscan->fdw_scan_tlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan), NULL);
}
else
{
@@ -1622,16 +1727,16 @@ set_foreignscan_references(PlannerInfo *root,
*/
fscan->scan.plan.targetlist =
fix_scan_list(root, fscan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan), NULL);
fscan->scan.plan.qual =
fix_scan_list(root, fscan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
fscan->fdw_exprs =
fix_scan_list(root, fscan->fdw_exprs,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
fscan->fdw_recheck_quals =
fix_scan_list(root, fscan->fdw_recheck_quals,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
}
fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset);
@@ -1690,20 +1795,20 @@ set_customscan_references(PlannerInfo *root,
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
cscan->custom_scan_tlist =
fix_scan_list(root, cscan->custom_scan_tlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan), NULL);
}
else
{
/* Adjust tlist, qual, custom_exprs in the standard way */
cscan->scan.plan.targetlist =
fix_scan_list(root, cscan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan), NULL);
cscan->scan.plan.qual =
fix_scan_list(root, cscan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan), NULL);
cscan->custom_exprs =
fix_scan_list(root, cscan->custom_exprs,
- rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan), NULL);
}
/* Adjust child plan-nodes recursively, if needed */
@@ -2111,6 +2216,95 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
return (Node *) bestplan;
}
+
+static inline void
+setup_intermediate_level_ctx(intermediate_level_context * ctx)
+{
+ ctx->level = 0;
+ ctx->level_added = false;
+}
+
+static inline void
+setup_intermediate_var_ref_ctx(intermediate_var_ref_context * ctx, Bitmapset **final_ref_attrs)
+{
+ ctx->existing_attrs = NULL;
+ ctx->final_ref_attrs = final_ref_attrs;
+}
+
+/*
+ * increase_level_for_pre_detoast
+ * Check if the given Expr could detoast a Var directly, if yes,
+ * increase the level and return true. otherwise return false;
+ */
+static inline void
+increase_level_for_pre_detoast(Node *node, intermediate_level_context * ctx)
+{
+ /* The following nodes is impossible to detoast a Var directly. */
+ if (IsA(node, List) || IsA(node, TargetEntry) || IsA(node, NullTest))
+ {
+ ctx->level_added = false;
+ }
+ else if (IsA(node, FuncExpr) && castNode(FuncExpr, node)->funcid == F_PG_COLUMN_COMPRESSION)
+ {
+ /* let's not detoast first so that pg_column_compression works. */
+ ctx->level_added = false;
+ }
+ else
+ {
+ ctx->level_added = true;
+ ctx->level += 1;
+ }
+}
+
+static inline void
+decreased_level_for_pre_detoast(intermediate_level_context * ctx)
+{
+ if (ctx->level_added)
+ ctx->level -= 1;
+
+ ctx->level_added = false;
+}
+
+/*
+ * add_pre_detoast_vars
+ * add the var's information into pre_detoast_attrs when the check is pass.
+ */
+static inline void
+add_pre_detoast_vars(intermediate_level_context * level_ctx,
+ intermediate_var_ref_context * ctx,
+ Var *var)
+{
+ int attno;
+
+ if (level_ctx->level <= 1 || ctx->final_ref_attrs == NULL || var->varattno <= 0)
+ return;
+
+ attno = var->varattno - 1;
+ if (bms_is_member(attno, ctx->existing_attrs))
+ {
+ /* not the first time to access it, add it to final result. */
+ *ctx->final_ref_attrs = bms_add_member(*ctx->final_ref_attrs, attno);
+ }
+ else
+ {
+ /* first time. */
+ ctx->existing_attrs = bms_add_member(ctx->existing_attrs, attno);
+
+ /*
+ * XXX:
+ *
+ * The above strategy doesn't help to detect if a Var is detoast
+ * twice. Reasons are: 1. the context is not maintain in Plan node
+ * level. so if it is detoast at targetlist and qual, we can't detect
+ * it. 2. even we can make it at plan node, it still doesn't help for
+ * the among-nodes case.
+ *
+ * So for now, I just disable it.
+ */
+ *ctx->final_ref_attrs = bms_add_member(*ctx->final_ref_attrs, attno);
+ }
+}
+
/*
* fix_scan_expr
* Do set_plan_references processing on a scan-level expression
@@ -2130,13 +2324,16 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
* if that seems safe.
*/
static Node *
-fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
+fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset,
+ double num_exec, Bitmapset **scan_reference_attrs)
{
fix_scan_expr_context context;
context.root = root;
context.rtoffset = rtoffset;
context.num_exec = num_exec;
+ setup_intermediate_level_ctx(&context.level_ctx);
+ setup_intermediate_var_ref_ctx(&context.scan_reference_attrs, scan_reference_attrs);
if (rtoffset != 0 ||
root->multiexpr_params != NIL ||
@@ -2167,8 +2364,13 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
static Node *
fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
{
+ Node *n;
+
if (node == NULL)
return NULL;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
if (IsA(node, Var))
{
Var *var = copyVar((Var *) node);
@@ -2186,10 +2388,18 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
var->varno += context->rtoffset;
if (var->varnosyn > 0)
var->varnosyn += context->rtoffset;
+
+ add_pre_detoast_vars(&context->level_ctx, &context->scan_reference_attrs, var);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) var;
}
if (IsA(node, Param))
- return fix_param_node(context->root, (Param *) node);
+ {
+ Node *n = fix_param_node(context->root, (Param *) node);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
+ }
if (IsA(node, Aggref))
{
Aggref *aggref = (Aggref *) node;
@@ -2200,7 +2410,10 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
if (aggparam != NULL)
{
/* Make a copy of the Param for paranoia's sake */
- return (Node *) copyObject(aggparam);
+ Node *n = (Node *) copyObject(aggparam);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
}
/* If no match, just fall through to process it normally */
}
@@ -2210,6 +2423,7 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
Assert(!IS_SPECIAL_VARNO(cexpr->cvarno));
cexpr->cvarno += context->rtoffset;
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) cexpr;
}
if (IsA(node, PlaceHolderVar))
@@ -2218,29 +2432,52 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
PlaceHolderVar *phv = (PlaceHolderVar *) node;
/* XXX can we assert something about phnullingrels? */
- return fix_scan_expr_mutator((Node *) phv->phexpr, context);
+ Node *n = fix_scan_expr_mutator((Node *) phv->phexpr, context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
}
if (IsA(node, AlternativeSubPlan))
- return fix_scan_expr_mutator(fix_alternative_subplan(context->root,
- (AlternativeSubPlan *) node,
- context->num_exec),
- context);
+ {
+ Node *n = fix_scan_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
+ }
fix_expr_common(context->root, node);
- return expression_tree_mutator(node, fix_scan_expr_mutator,
- (void *) context);
+ n = expression_tree_mutator(node, fix_scan_expr_mutator, (void *) context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
}
static bool
fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
{
+ bool ret;
+
if (node == NULL)
return false;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
+ if (IsA(node, Var))
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->scan_reference_attrs,
+ castNode(Var, node));
+ }
Assert(!(IsA(node, Var) && ((Var *) node)->varno == ROWID_VAR));
Assert(!IsA(node, PlaceHolderVar));
Assert(!IsA(node, AlternativeSubPlan));
fix_expr_common(context->root, node);
- return expression_tree_walker(node, fix_scan_expr_walker,
- (void *) context);
+ ret = expression_tree_walker(node, fix_scan_expr_walker,
+ (void *) context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret;
}
/*
@@ -2276,7 +2513,10 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs
+ );
/* Now do join-type-specific stuff */
if (IsA(join, NestLoop))
@@ -2323,7 +2563,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
}
else if (IsA(join, HashJoin))
{
@@ -2336,7 +2578,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
/*
* HashJoin's hashkeys are used to look for matching tuples from its
@@ -2368,7 +2612,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
(join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
- NUM_EXEC_TLIST((Plan *) join));
+ NUM_EXEC_TLIST((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
join->plan.qual = fix_join_expr(root,
join->plan.qual,
outer_itlist,
@@ -2376,8 +2622,20 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
(join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
- NUM_EXEC_QUAL((Plan *) join));
-
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
+
+ join->plan.forbid_pre_detoast_vars = fix_join_expr(root,
+ join->plan.forbid_pre_detoast_vars,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset,
+ (join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
+ NUM_EXEC_TLIST((Plan *) join),
+ NULL,
+ NULL);
pfree(outer_itlist);
pfree(inner_itlist);
}
@@ -3010,9 +3268,12 @@ fix_join_expr(PlannerInfo *root,
Index acceptable_rel,
int rtoffset,
NullingRelsMatch nrm_match,
- double num_exec)
+ double num_exec,
+ Bitmapset **outer_reference_attrs,
+ Bitmapset **inner_reference_attrs)
{
fix_join_expr_context context;
+ List *ret;
context.root = root;
context.outer_itlist = outer_itlist;
@@ -3021,16 +3282,30 @@ fix_join_expr(PlannerInfo *root,
context.rtoffset = rtoffset;
context.nrm_match = nrm_match;
context.num_exec = num_exec;
- return (List *) fix_join_expr_mutator((Node *) clauses, &context);
+
+ setup_intermediate_level_ctx(&context.level_ctx);
+ setup_intermediate_var_ref_ctx(&context.outer_reference_attrs, outer_reference_attrs);
+ setup_intermediate_var_ref_ctx(&context.inner_reference_attrs, inner_reference_attrs);
+
+ ret = (List *) fix_join_expr_mutator((Node *) clauses, &context);
+
+ bms_free(context.outer_reference_attrs.existing_attrs);
+ bms_free(context.inner_reference_attrs.existing_attrs);
+
+ return ret;
}
static Node *
fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
{
Var *newvar;
+ Node *ret_node;
if (node == NULL)
return NULL;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
if (IsA(node, Var))
{
Var *var = (Var *) node;
@@ -3044,7 +3319,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->rtoffset,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* then in the inner. */
@@ -3056,7 +3337,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->rtoffset,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* If it's for acceptable_rel, adjust and return it */
@@ -3066,6 +3353,9 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
var->varno += context->rtoffset;
if (var->varnosyn > 0)
var->varnosyn += context->rtoffset;
+ /* XXX acceptable_rel? we can ignore it for safety. */
+ decreased_level_for_pre_detoast(&context->level_ctx);
+
return (Node *) var;
}
@@ -3084,22 +3374,38 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
OUTER_VAR,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
if (context->inner_itlist && context->inner_itlist->has_ph_vars)
{
+
newvar = search_indexed_tlist_for_phv(phv,
context->inner_itlist,
INNER_VAR,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* If not supplied by input plans, evaluate the contained expr */
/* XXX can we assert something about phnullingrels? */
- return fix_join_expr_mutator((Node *) phv->phexpr, context);
+ ret_node = fix_join_expr_mutator((Node *) phv->phexpr, context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
}
+
/* Try matching more complex expressions too, if tlists have any */
if (context->outer_itlist && context->outer_itlist->has_non_vars)
{
@@ -3107,7 +3413,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->outer_itlist,
OUTER_VAR);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
if (context->inner_itlist && context->inner_itlist->has_non_vars)
{
@@ -3115,20 +3427,36 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->inner_itlist,
INNER_VAR);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* Special cases (apply only AFTER failing to match to lower tlist) */
if (IsA(node, Param))
- return fix_param_node(context->root, (Param *) node);
+ {
+ ret_node = fix_param_node(context->root, (Param *) node);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
+ }
if (IsA(node, AlternativeSubPlan))
- return fix_join_expr_mutator(fix_alternative_subplan(context->root,
- (AlternativeSubPlan *) node,
- context->num_exec),
- context);
+ {
+ ret_node = fix_join_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
+ }
fix_expr_common(context->root, node);
- return expression_tree_mutator(node,
- fix_join_expr_mutator,
- (void *) context);
+ ret_node = expression_tree_mutator(node,
+ fix_join_expr_mutator,
+ (void *) context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
}
/*
@@ -3163,7 +3491,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
* varno = newvarno, varattno = resno of corresponding targetlist element.
* The original tree is not modified.
*/
-static Node *
+static Node * /* XXX: shall I care about this for shared
+ * detoast optimization? */
fix_upper_expr(PlannerInfo *root,
Node *node,
indexed_tlist *subplan_itlist,
@@ -3318,7 +3647,10 @@ set_returning_clause_references(PlannerInfo *root,
resultRelation,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_TLIST(topplan));
+ NUM_EXEC_TLIST(topplan),
+ NULL,
+ NULL
+ );
pfree(itlist);
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 048573c2bc..39c33e51b1 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -77,6 +77,11 @@ typedef enum ExprEvalOp
EEOP_OUTER_VAR,
EEOP_SCAN_VAR,
+ /* compute non-system Var value with shared-detoast-datum logic */
+ EEOP_INNER_VAR_TOAST,
+ EEOP_OUTER_VAR_TOAST,
+ EEOP_SCAN_VAR_TOAST,
+
/* compute system Var value */
EEOP_INNER_SYSVAR,
EEOP_OUTER_SYSVAR,
@@ -826,5 +831,6 @@ extern void ExecEvalAggOrderedTransDatum(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecSlotDetoastDatumExternal(TupleTableSlot *slot, int attnum);
#endif /* EXEC_EXPR_H */
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 4210d6d838..258ff8906e 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,6 +18,7 @@
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/tupdesc.h"
+#include "nodes/bitmapset.h"
#include "storage/buf.h"
/*----------
@@ -128,6 +129,11 @@ typedef struct TupleTableSlot
MemoryContext tts_mcxt; /* slot itself is in this context */
ItemPointerData tts_tid; /* stored tuple's tid */
Oid tts_tableOid; /* table oid of tuple */
+
+ /*
+ * The attributes populated by EEOP_{INNER/OUTER/SCAN}_VAR_TOAST step.
+ */
+ Bitmapset *pre_detoasted_attrs;
} TupleTableSlot;
/* routines for a TupleTableSlot implementation */
@@ -425,12 +431,38 @@ slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
return slot->tts_ops->getsysattr(slot, attnum, isnull);
}
+static inline void
+ExecFreePreDetoastDatum(TupleTableSlot *slot)
+{
+ int attnum;
+
+ if (bms_is_empty(slot->pre_detoasted_attrs))
+ return;
+
+ attnum = -1;
+ /* free the memory used by pre-detoasted datum and reset the flags. */
+ while ((attnum = bms_next_member(slot->pre_detoasted_attrs, attnum)) >= 0)
+ {
+ pfree((void *) slot->tts_values[attnum]);
+ }
+
+ /*
+ * bms_free each time cost too much, so just zero these bits and keep its
+ * memory, just like what we did for TupleTableSlot. but.. see the
+ * comments about the bms_zero.
+ */
+ bms_zero(slot->pre_detoasted_attrs);
+}
+
+
/*
* ExecClearTuple - clear the slot's contents
*/
static inline TupleTableSlot *
ExecClearTuple(TupleTableSlot *slot)
{
+ ExecFreePreDetoastDatum(slot);
+
slot->tts_ops->clear(slot);
return slot;
@@ -449,6 +481,10 @@ ExecClearTuple(TupleTableSlot *slot)
static inline void
ExecMaterializeSlot(TupleTableSlot *slot)
{
+ /*
+ * XXX: pre_detoasted_attrs doesn't dependent on any external storage, so
+ * nothing should be done here.
+ */
slot->tts_ops->materialize(slot);
}
@@ -486,6 +522,30 @@ ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
dstslot->tts_ops->copyslot(dstslot, srcslot);
+ /* Assert this assumption the below code depends on. */
+ Assert(dstslot->tts_nvalid == 0 ||
+ dstslot->tts_nvalid == srcslot->tts_nvalid);
+
+ if (dstslot->tts_nvalid == srcslot->tts_nvalid
+ && !bms_is_empty(srcslot->pre_detoasted_attrs))
+ {
+ int attnum = -1;
+ MemoryContext old = MemoryContextSwitchTo(dstslot->tts_mcxt);
+
+ dstslot->pre_detoasted_attrs = bms_copy(srcslot->pre_detoasted_attrs);
+
+ while ((attnum = bms_next_member(dstslot->pre_detoasted_attrs, attnum)) >= 0)
+ {
+ struct varlena *datum = (struct varlena *) srcslot->tts_values[attnum];
+ Size len;
+
+ Assert(!VARATT_IS_EXTENDED(datum));
+ len = VARSIZE(datum);
+ dstslot->tts_values[attnum] = (Datum) palloc(len);
+ memcpy((void *) dstslot->tts_values[attnum], datum, len);
+ }
+ MemoryContextSwitchTo(old);
+ }
return dstslot;
}
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 161243b2d0..402ff02027 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -113,6 +113,7 @@ extern Bitmapset *bms_add_range(Bitmapset *a, int lower, int upper);
extern Bitmapset *bms_int_members(Bitmapset *a, const Bitmapset *b);
extern Bitmapset *bms_del_members(Bitmapset *a, const Bitmapset *b);
extern Bitmapset *bms_join(Bitmapset *a, Bitmapset *b);
+extern void bms_zero(Bitmapset *a);
/* support for iterating through the integer elements of a set: */
extern int bms_next_member(const Bitmapset *a, int prevbit);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 5d7f17dee0..c3f7d19ba2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1474,6 +1474,7 @@ typedef struct ScanState
Relation ss_currentRelation;
struct TableScanDescData *ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
+ Bitmapset *scan_pre_detoast_attrs;
} ScanState;
/* ----------------
@@ -2003,6 +2004,8 @@ typedef struct JoinState
bool single_match; /* True if we should skip to next outer tuple
* after finding one inner match */
ExprState *joinqual; /* JOIN quals (in addition to ps.qual) */
+ Bitmapset *outer_pre_detoast_attrs;
+ Bitmapset *inner_pre_detoast_attrs;
} JoinState;
/* ----------------
@@ -2764,4 +2767,6 @@ typedef struct LimitState
TupleTableSlot *last_slot; /* slot for evaluation of ties */
} LimitState;
+extern void SetPredetoastAttrsForJoin(JoinState *joinstate);
+extern void SetPredetoastAttrsForScan(ScanState *scanstate);
#endif /* EXECNODES_H */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index d40af8e59f..de4f3f190a 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -169,6 +169,13 @@ typedef struct Plan
*/
Bitmapset *extParam;
Bitmapset *allParam;
+
+ /*
+ * A list of Vars which should not apply the shared-detoast-datum logic
+ * since the upper nodes like Sort/Hash want the tuples as small as
+ * possible. Its a subset of targetlist in each Plan node.
+ */
+ List *forbid_pre_detoast_vars;
} Plan;
/* ----------------
@@ -385,6 +392,13 @@ typedef struct Scan
Plan plan;
Index scanrelid; /* relid is index into the range table */
+
+ /*
+ * Records of var's varattno - 1 where the Var is accessed indirectly by
+ * any expression, like a > 3. However a IS [NOT] NULL is not included
+ * since it doesn't access the tts_values[*] at all.
+ */
+ Bitmapset *reference_attrs;
} Scan;
/* ----------------
@@ -789,6 +803,14 @@ typedef struct Join
JoinType jointype;
bool inner_unique;
List *joinqual; /* JOIN quals (in addition to plan.qual) */
+
+ /*
+ * Records of var's varattno - 1 where the Var is accessed indirectly by
+ * any expression, like a > 3. However a IS [NOT] NULL is not included
+ * since it doesn't access the tts_values[*] at all.
+ */
+ Bitmapset *outer_reference_attrs;
+ Bitmapset *inner_reference_attrs;
} Join;
/* ----------------
@@ -869,6 +891,14 @@ typedef struct HashJoin
* perform lookups in the hashtable over the inner plan.
*/
List *hashkeys;
+
+ /*
+ * keep the small tlist information in plan for the shared-detoast datum
+ * logic. If left_small_tlist is true, then all the datum in outerPlan
+ * should not apply that logic, used for maintaining
+ * forbid_pre_detoast_vars fields in Plan.
+ */
+ bool left_small_tlist;
} HashJoin;
/* ----------------
@@ -1588,4 +1618,24 @@ typedef enum MonotonicFunction
MONOTONICFUNC_BOTH = MONOTONICFUNC_INCREASING | MONOTONICFUNC_DECREASING,
} MonotonicFunction;
+static inline bool
+is_join_plan(Plan *plan)
+{
+ return (plan != NULL) && (IsA(plan, NestLoop) || IsA(plan, HashJoin) || IsA(plan, MergeJoin));
+}
+
+static inline bool
+is_scan_plan(Plan *plan)
+{
+ return (plan != NULL) &&
+ (IsA(plan, SeqScan) ||
+ IsA(plan, SampleScan) ||
+ IsA(plan, IndexScan) ||
+ IsA(plan, IndexOnlyScan) ||
+ IsA(plan, BitmapIndexScan) ||
+ IsA(plan, BitmapHeapScan) ||
+ IsA(plan, TidScan) ||
+ IsA(plan, SubqueryScan));
+}
+
#endif /* PLANNODES_H */
diff --git a/src/test/regress/sql/shared_detoast_slow.sql b/src/test/regress/sql/shared_detoast_slow.sql
new file mode 100644
index 0000000000..beecaa6bc5
--- /dev/null
+++ b/src/test/regress/sql/shared_detoast_slow.sql
@@ -0,0 +1,70 @@
+create table t1(a text, b text, c text);
+create table t2(a text, b text, c text);
+create table t3(a text, b text, c text);
+
+insert into t1 select i, i, i from generate_series(1, 1000000)i;
+insert into t2 select i, i, i from generate_series(1, 1000000)i;
+insert into t3 select i, i, i from generate_series(1, 1000000)i;
+
+create index on t1(c);
+
+analyze t1;
+analyze t2;
+analyze t3;
+
+-- Turn off jit first, reasons:
+-- 1. JIT is not adapted for this feature, it may cause crash on jit.
+-- 2. more logging for this feature is enabled when jit=off
+set jit to off;
+
+explain (verbose) select * from t1 where b > 'a';
+
+
+-- NullTest has nothing with tts_values, so its access to toast value
+-- should be ignored.
+explain (verbose) select * from t1 where b is NULL and c is not null;
+
+-- b can't be shared-detoasted since it would make the work_mem bigger.
+explain (verbose) select * from t1 where b > 'a' order by c;
+
+-- b CAN be shared-detoasted since it would NOT make the work_mem bigger.
+-- but compared with the old behavior, it cause the lifespan of the
+-- 'detoast datum'
+-- longer, in the old behavior, it is reset becase of ExecQualAndReset.
+explain (verbose) select a, c from t1 where b > 'a' order by c;
+
+-- The detoast only happen at the join stage.
+explain (verbose) select * from t1 join t2 using(b);
+
+--
+explain (verbose) select * from t1 join t2 using(b) where t1.c > '3';
+
+explain (verbose)
+select t3.*
+from t1, t2, t3
+where t2.c > '999999999999999'
+and t2.c = t1.c
+and t3.b = t1.b;
+
+
+-- t2.b the innerHash can't be pre detoast due to Hash.
+-- t1.b the outerPlan can't be pre detoast due to num_batch.
+explain (verbose) select * from t1 join t2 using(b);
+
+-- Increase the work_mem so that the num_batch = 1, then
+-- the t1.b in outerPlan can be pre-detoast.
+
+set work_mem to '1GB';
+explain (verbose) select * from t1 join t2 using(b);
+reset work_mem;
+
+-- Show even if a datum is under a hash node, but it is
+-- not DIRECTLY input of the Hash, it's still able to be
+-- pre detoasted.
+explain (verbose)
+select t3.*
+from t1, t2, t3
+where t2.c > '999999999999999'
+and t2.c = t1.c
+and t3.b = t1.b
+and t1.b > '0';
--
2.34.1
^ permalink raw reply [nested|flat] 11+ messages in thread
* Re: Shared detoast Datum proposal
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
@ 2024-01-06 15:12 ` vignesh C <[email protected]>
2024-01-07 01:10 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
0 siblings, 1 reply; 11+ messages in thread
From: vignesh C @ 2024-01-06 15:12 UTC (permalink / raw)
To: Andy Fan <[email protected]>; +Cc: [email protected] <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>
On Mon, 1 Jan 2024 at 19:26, Andy Fan <[email protected]> wrote:
>
>
> Andy Fan <[email protected]> writes:
>
> >
> > Some Known issues:
> > ------------------
> >
> > 1. Currently only Scan & Join nodes are considered for this feature.
> > 2. JIT is not adapted for this purpose yet.
>
> JIT is adapted for this feature in v2. Any feedback is welcome.
One of the tests was aborted at CFBOT [1] with:
[09:47:00.735] dumping /tmp/cores/postgres-11-28182.core for
/tmp/cirrus-ci-build/build/tmp_install//usr/local/pgsql/bin/postgres
[09:47:01.035] [New LWP 28182]
[09:47:01.748] [Thread debugging using libthread_db enabled]
[09:47:01.748] Using host libthread_db library
"/lib/x86_64-linux-gnu/libthread_db.so.1".
[09:47:09.392] Core was generated by `postgres: postgres regression
[local] SELECT '.
[09:47:09.392] Program terminated with signal SIGSEGV, Segmentation fault.
[09:47:09.392] #0 0x00007fa4eed4a5a1 in ?? ()
[09:47:11.123]
[09:47:11.123] Thread 1 (Thread 0x7fa4f8050a40 (LWP 28182)):
[09:47:11.123] #0 0x00007fa4eed4a5a1 in ?? ()
[09:47:11.123] No symbol table info available.
...
...
[09:47:11.123] #4 0x00007fa4ebc7a186 in LLVMOrcGetSymbolAddress () at
/build/llvm-toolchain-11-HMpQvg/llvm-toolchain-11-11.0.1/llvm/lib/ExecutionEngine/Orc/OrcCBindings.cpp:124
[09:47:11.123] No locals.
[09:47:11.123] #5 0x00007fa4eed6fc7a in llvm_get_function
(context=0x564b1813a8a0, funcname=0x7fa4eed4a570 "AWAVATSH\201",
<incomplete sequence \354\210>) at
../src/backend/jit/llvm/llvmjit.c:460
[09:47:11.123] addr = 94880527996960
[09:47:11.123] __func__ = "llvm_get_function"
[09:47:11.123] #6 0x00007fa4eed902e1 in ExecRunCompiledExpr
(state=0x0, econtext=0x564b18269d20, isNull=0x7ffc11054d5f) at
../src/backend/jit/llvm/llvmjit_expr.c:2577
[09:47:11.123] cstate = <optimized out>
[09:47:11.123] func = 0x564b18269d20
[09:47:11.123] #7 0x0000564b1698e614 in ExecEvalExprSwitchContext
(isNull=0x7ffc11054d5f, econtext=0x564b18269d20, state=0x564b182ad820)
at ../src/include/executor/executor.h:355
[09:47:11.123] retDatum = <optimized out>
[09:47:11.123] oldContext = 0x564b182680d0
[09:47:11.123] retDatum = <optimized out>
[09:47:11.123] oldContext = <optimized out>
[09:47:11.123] #8 ExecProject (projInfo=0x564b182ad818) at
../src/include/executor/executor.h:389
[09:47:11.123] econtext = 0x564b18269d20
[09:47:11.123] state = 0x564b182ad820
[09:47:11.123] slot = 0x564b182ad788
[09:47:11.123] isnull = false
[09:47:11.123] econtext = <optimized out>
[09:47:11.123] state = <optimized out>
[09:47:11.123] slot = <optimized out>
[09:47:11.123] isnull = <optimized out>
[09:47:11.123] #9 ExecMergeJoin (pstate=<optimized out>) at
../src/backend/executor/nodeMergejoin.c:836
[09:47:11.123] node = <optimized out>
[09:47:11.123] joinqual = 0x0
[09:47:11.123] otherqual = 0x0
[09:47:11.123] qualResult = <optimized out>
[09:47:11.123] compareResult = <optimized out>
[09:47:11.123] innerPlan = <optimized out>
[09:47:11.123] innerTupleSlot = <optimized out>
[09:47:11.123] outerPlan = <optimized out>
[09:47:11.123] outerTupleSlot = <optimized out>
[09:47:11.123] econtext = 0x564b18269d20
[09:47:11.123] doFillOuter = false
[09:47:11.123] doFillInner = false
[09:47:11.123] __func__ = "ExecMergeJoin"
[09:47:11.123] #10 0x0000564b169275b9 in ExecProcNodeFirst
(node=0x564b18269db0) at ../src/backend/executor/execProcnode.c:464
[09:47:11.123] No locals.
[09:47:11.123] #11 0x0000564b169a2675 in ExecProcNode
(node=0x564b18269db0) at ../src/include/executor/executor.h:273
[09:47:11.123] No locals.
[09:47:11.123] #12 ExecRecursiveUnion (pstate=0x564b182684a0) at
../src/backend/executor/nodeRecursiveunion.c:115
[09:47:11.123] node = 0x564b182684a0
[09:47:11.123] outerPlan = 0x564b18268d00
[09:47:11.123] innerPlan = 0x564b18269db0
[09:47:11.123] plan = 0x564b182ddc78
[09:47:11.123] slot = <optimized out>
[09:47:11.123] isnew = false
[09:47:11.123] #13 0x0000564b1695a421 in ExecProcNode
(node=0x564b182684a0) at ../src/include/executor/executor.h:273
[09:47:11.123] No locals.
[09:47:11.123] #14 CteScanNext (node=0x564b183a6830) at
../src/backend/executor/nodeCtescan.c:103
[09:47:11.123] cteslot = <optimized out>
[09:47:11.123] estate = <optimized out>
[09:47:11.123] dir = ForwardScanDirection
[09:47:11.123] forward = true
[09:47:11.123] tuplestorestate = 0x564b183a5cd0
[09:47:11.123] eof_tuplestore = <optimized out>
[09:47:11.123] slot = 0x564b183a6be0
[09:47:11.123] #15 0x0000564b1692e22b in ExecScanFetch
(node=node@entry=0x564b183a6830,
accessMtd=accessMtd@entry=0x564b1695a183 <CteScanNext>,
recheckMtd=recheckMtd@entry=0x564b16959db3 <CteScanRecheck>) at
../src/backend/executor/execScan.c:132
[09:47:11.123] estate = <optimized out>
[09:47:11.123] #16 0x0000564b1692e332 in ExecScan
(node=0x564b183a6830, accessMtd=accessMtd@entry=0x564b1695a183
<CteScanNext>, recheckMtd=recheckMtd@entry=0x564b16959db3
<CteScanRecheck>) at ../src/backend/executor/execScan.c:181
[09:47:11.123] econtext = 0x564b183a6b50
[09:47:11.123] qual = 0x0
[09:47:11.123] projInfo = 0x0
[09:47:11.123] #17 0x0000564b1695a5ea in ExecCteScan
(pstate=<optimized out>) at ../src/backend/executor/nodeCtescan.c:164
[09:47:11.123] node = <optimized out>
[09:47:11.123] #18 0x0000564b169a81da in ExecProcNode
(node=0x564b183a6830) at ../src/include/executor/executor.h:273
[09:47:11.123] No locals.
[09:47:11.123] #19 ExecSort (pstate=0x564b183a6620) at
../src/backend/executor/nodeSort.c:149
[09:47:11.123] plannode = 0x564b182dfb78
[09:47:11.123] outerNode = 0x564b183a6830
[09:47:11.123] tupDesc = <optimized out>
[09:47:11.123] tuplesortopts = <optimized out>
[09:47:11.123] node = 0x564b183a6620
[09:47:11.123] estate = 0x564b182681d0
[09:47:11.123] dir = ForwardScanDirection
[09:47:11.123] tuplesortstate = 0x564b18207ff0
[09:47:11.123] slot = <optimized out>
[09:47:11.123] #20 0x0000564b169275b9 in ExecProcNodeFirst
(node=0x564b183a6620) at ../src/backend/executor/execProcnode.c:464
[09:47:11.123] No locals.
[09:47:11.123] #21 0x0000564b16913d01 in ExecProcNode
(node=0x564b183a6620) at ../src/include/executor/executor.h:273
[09:47:11.123] No locals.
[09:47:11.123] #22 ExecutePlan (estate=estate@entry=0x564b182681d0,
planstate=0x564b183a6620, use_parallel_mode=<optimized out>,
operation=operation@entry=CMD_SELECT,
sendTuples=sendTuples@entry=true, numberTuples=numberTuples@entry=0,
direction=ForwardScanDirection, dest=0x564b182e2728,
execute_once=true) at ../src/backend/executor/execMain.c:1670
[09:47:11.123] slot = <optimized out>
[09:47:11.123] current_tuple_count = 0
[09:47:11.123] #23 0x0000564b16914024 in standard_ExecutorRun
(queryDesc=0x564b181ba200, direction=ForwardScanDirection, count=0,
execute_once=<optimized out>) at
../src/backend/executor/execMain.c:365
[09:47:11.123] estate = 0x564b182681d0
[09:47:11.123] operation = CMD_SELECT
[09:47:11.123] dest = 0x564b182e2728
[09:47:11.123] sendTuples = true
[09:47:11.123] oldcontext = 0x564b181ba100
[09:47:11.123] __func__ = "standard_ExecutorRun"
[09:47:11.123] #24 0x0000564b1691418f in ExecutorRun
(queryDesc=queryDesc@entry=0x564b181ba200,
direction=direction@entry=ForwardScanDirection, count=count@entry=0,
execute_once=<optimized out>) at
../src/backend/executor/execMain.c:309
[09:47:11.123] No locals.
[09:47:11.123] #25 0x0000564b16d208af in PortalRunSelect
(portal=portal@entry=0x564b1817ae10, forward=forward@entry=true,
count=0, count@entry=9223372036854775807,
dest=dest@entry=0x564b182e2728) at ../src/backend/tcop/pquery.c:924
[09:47:11.123] queryDesc = 0x564b181ba200
[09:47:11.123] direction = <optimized out>
[09:47:11.123] nprocessed = <optimized out>
[09:47:11.123] __func__ = "PortalRunSelect"
[09:47:11.123] #26 0x0000564b16d2405b in PortalRun
(portal=portal@entry=0x564b1817ae10,
count=count@entry=9223372036854775807,
isTopLevel=isTopLevel@entry=true, run_once=run_once@entry=true,
dest=dest@entry=0x564b182e2728, altdest=altdest@entry=0x564b182e2728,
qc=0x7ffc110551e0) at ../src/backend/tcop/pquery.c:768
[09:47:11.123] _save_exception_stack = 0x7ffc11055290
[09:47:11.123] _save_context_stack = 0x0
[09:47:11.123] _local_sigjmp_buf = {{__jmpbuf = {1,
3431825231787999889, 94880528213800, 94880526221072, 94880526741008,
94880526221000, -3433879442176195951, -8991885832768699759},
__mask_was_saved = 0, __saved_mask = {__val = {140720594047343, 688,
94880526749216, 94880511205302, 1, 140720594047343, 94880526999808, 8,
94880526213088, 112, 179, 94880526221072, 94880526221048,
94880526221000, 94880508502828, 2}}}}
[09:47:11.123] _do_rethrow = <optimized out>
[09:47:11.123] result = <optimized out>
[09:47:11.123] nprocessed = <optimized out>
[09:47:11.123] saveTopTransactionResourceOwner = 0x564b18139ad8
[09:47:11.123] saveTopTransactionContext = 0x564b181229f0
[09:47:11.123] saveActivePortal = 0x0
[09:47:11.123] saveResourceOwner = 0x564b18139ad8
[09:47:11.123] savePortalContext = 0x0
[09:47:11.123] saveMemoryContext = 0x564b181229f0
[09:47:11.123] __func__ = "PortalRun"
[09:47:11.123] #27 0x0000564b16d1d098 in exec_simple_query
(query_string=query_string@entry=0x564b180fa0e0 "with recursive
search_graph(f, t, label) as (\n\tselect * from graph0 g\n\tunion
all\n\tselect g.*\n\tfrom graph0 g, search_graph sg\n\twhere g.f =
sg.t\n) search depth first by f, t set seq\nselect * from search"...)
at ../src/backend/tcop/postgres.c:1273
[09:47:11.123] cmdtaglen = 6
[09:47:11.123] snapshot_set = <optimized out>
[09:47:11.123] per_parsetree_context = 0x0
[09:47:11.123] plantree_list = 0x564b182e26d8
[09:47:11.123] parsetree = 0x564b180fbec8
[09:47:11.123] commandTag = <optimized out>
[09:47:11.123] qc = {commandTag = CMDTAG_UNKNOWN, nprocessed = 0}
[09:47:11.123] querytree_list = <optimized out>
[09:47:11.123] portal = 0x564b1817ae10
[09:47:11.123] receiver = 0x564b182e2728
[09:47:11.123] format = 0
[09:47:11.123] cmdtagname = <optimized out>
[09:47:11.123] parsetree_item__state = {l = <optimized out>, i
= <optimized out>}
[09:47:11.123] dest = DestRemote
[09:47:11.123] oldcontext = 0x564b181229f0
[09:47:11.123] parsetree_list = 0x564b180fbef8
[09:47:11.123] parsetree_item = 0x564b180fbf10
[09:47:11.123] save_log_statement_stats = false
[09:47:11.123] was_logged = false
[09:47:11.123] use_implicit_block = false
[09:47:11.123] msec_str =
"\004\000\000\000\000\000\000\000\346[\376\026KV\000\000pR\005\021\374\177\000\000\335\000\000\000\000\000\000"
[09:47:11.123] __func__ = "exec_simple_query"
[09:47:11.123] #28 0x0000564b16d1fe33 in PostgresMain
(dbname=<optimized out>, username=<optimized out>) at
../src/backend/tcop/postgres.c:4653
[09:47:11.123] query_string = 0x564b180fa0e0 "with recursive
search_graph(f, t, label) as (\n\tselect * from graph0 g\n\tunion
all\n\tselect g.*\n\tfrom graph0 g, search_graph sg\n\twhere g.f =
sg.t\n) search depth first by f, t set seq\nselect * from search"...
[09:47:11.123] firstchar = <optimized out>
[09:47:11.123] input_message = {data = 0x564b180fa0e0 "with
recursive search_graph(f, t, label) as (\n\tselect * from graph0
g\n\tunion all\n\tselect g.*\n\tfrom graph0 g, search_graph
sg\n\twhere g.f = sg.t\n) search depth first by f, t set seq\nselect *
from search"..., len = 221, maxlen = 1024, cursor = 221}
[09:47:11.123] local_sigjmp_buf = {{__jmpbuf =
{94880520543032, -8991887800862096751, 0, 4, 140720594048004, 1,
-3433879442247499119, -8991885813233991023}, __mask_was_saved = 1,
__saved_mask = {__val = {4194304, 1, 140346553036196, 94880526187920,
15616, 15680, 94880508418872, 0, 94880526187920, 15616,
94880520537224, 4, 140720594048004, 1, 94880508502235, 1}}}}
[09:47:11.123] send_ready_for_query = false
[09:47:11.123] idle_in_transaction_timeout_enabled = false
[09:47:11.123] idle_session_timeout_enabled = false
[09:47:11.123] __func__ = "PostgresMain"
[09:47:11.123] #29 0x0000564b16bcc4e4 in BackendRun
(port=port@entry=0x564b18126f50) at
../src/backend/postmaster/postmaster.c:4464
[1] - https://cirrus-ci.com/task/4765094966460416
Regards,
Vignesh
^ permalink raw reply [nested|flat] 11+ messages in thread
* Re: Shared detoast Datum proposal
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-06 15:12 ` Re: Shared detoast Datum proposal vignesh C <[email protected]>
@ 2024-01-07 01:10 ` Andy Fan <[email protected]>
2024-01-08 09:52 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
0 siblings, 1 reply; 11+ messages in thread
From: Andy Fan @ 2024-01-07 01:10 UTC (permalink / raw)
To: vignesh C <[email protected]>; +Cc: [email protected] <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>
Hi,
vignesh C <[email protected]> writes:
> On Mon, 1 Jan 2024 at 19:26, Andy Fan <[email protected]> wrote:
>>
>>
>> Andy Fan <[email protected]> writes:
>>
>> >
>> > Some Known issues:
>> > ------------------
>> >
>> > 1. Currently only Scan & Join nodes are considered for this feature.
>> > 2. JIT is not adapted for this purpose yet.
>>
>> JIT is adapted for this feature in v2. Any feedback is welcome.
>
> One of the tests was aborted at CFBOT [1] with:
> [09:47:00.735] dumping /tmp/cores/postgres-11-28182.core for
> /tmp/cirrus-ci-build/build/tmp_install//usr/local/pgsql/bin/postgres
> [09:47:01.035] [New LWP 28182]
There was a bug in JIT part, here is the fix. Thanks for taking care of
this!
--
Best Regards
Andy Fan
Attachments:
[text/x-diff] v3-0001-shared-detoast-feature.patch (75.0K, ../../[email protected]/2-v3-0001-shared-detoast-feature.patch)
download | inline diff:
From 06a212ad6b6254cbd38dbacc409165300ff7c677 Mon Sep 17 00:00:00 2001
From: "yizhi.fzh" <[email protected]>
Date: Wed, 27 Dec 2023 18:43:56 +0800
Subject: [PATCH v3 1/1] shared detoast feature.
---
src/backend/executor/execExpr.c | 65 ++-
src/backend/executor/execExprInterp.c | 181 +++++++
src/backend/executor/execTuples.c | 130 +++++
src/backend/executor/execUtils.c | 5 +
src/backend/executor/nodeHashjoin.c | 2 +
src/backend/executor/nodeMergejoin.c | 2 +
src/backend/executor/nodeNestloop.c | 1 +
src/backend/jit/llvm/llvmjit_expr.c | 26 +-
src/backend/jit/llvm/llvmjit_types.c | 1 +
src/backend/nodes/bitmapset.c | 13 +
src/backend/optimizer/plan/createplan.c | 73 ++-
src/backend/optimizer/plan/setrefs.c | 536 +++++++++++++++----
src/include/executor/execExpr.h | 6 +
src/include/executor/tuptable.h | 60 +++
src/include/nodes/bitmapset.h | 1 +
src/include/nodes/execnodes.h | 5 +
src/include/nodes/plannodes.h | 50 ++
src/test/regress/sql/shared_detoast_slow.sql | 70 +++
18 files changed, 1119 insertions(+), 108 deletions(-)
create mode 100644 src/test/regress/sql/shared_detoast_slow.sql
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 91df2009be..4aeec8419f 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -932,22 +932,81 @@ ExecInitExprRec(Expr *node, ExprState *state,
}
else
{
+ int attnum;
+ Plan *plan = state->parent ? state->parent->plan : NULL;
+
/* regular user column */
scratch.d.var.attnum = variable->varattno - 1;
scratch.d.var.vartype = variable->vartype;
+ attnum = scratch.d.var.attnum;
+
switch (variable->varno)
{
case INNER_VAR:
- scratch.opcode = EEOP_INNER_VAR;
+
+ if (is_join_plan(plan) &&
+ bms_is_member(attnum,
+ ((JoinState *) state->parent)->inner_pre_detoast_attrs))
+ {
+ /* debug purpose. */
+ if (!jit_enabled)
+ {
+ elog(INFO,
+ "EEOP_INNER_VAR_TOAST: flags = %d costs=%.2f..%.2f, attnum: %d",
+ state->flags,
+ plan->startup_cost,
+ plan->total_cost,
+ attnum);
+ }
+ scratch.opcode = EEOP_INNER_VAR_TOAST;
+ }
+ else
+ {
+ scratch.opcode = EEOP_INNER_VAR;
+ }
break;
case OUTER_VAR:
- scratch.opcode = EEOP_OUTER_VAR;
+ if (is_join_plan(plan) &&
+ bms_is_member(attnum,
+ ((JoinState *) state->parent)->outer_pre_detoast_attrs))
+ {
+ /* debug purpose. */
+ if (!jit_enabled)
+ {
+ elog(INFO,
+ "EEOP_OUTER_VAR_TOAST: flags = %u costs=%.2f..%.2f, attnum: %d",
+ state->flags,
+ plan->startup_cost,
+ plan->total_cost,
+ attnum);
+ }
+ scratch.opcode = EEOP_OUTER_VAR_TOAST;
+ }
+ else
+ scratch.opcode = EEOP_OUTER_VAR;
break;
/* INDEX_VAR is handled by default case */
default:
- scratch.opcode = EEOP_SCAN_VAR;
+ if (is_scan_plan(plan) && bms_is_member(
+ attnum,
+ ((ScanState *) state->parent)->scan_pre_detoast_attrs))
+ {
+ if (!jit_enabled)
+ {
+ elog(INFO,
+ "EEOP_SCAN_VAR_TOAST: flags = %u costs=%.2f..%.2f, scanId: %d, attnum: %d",
+ state->flags,
+ plan->startup_cost,
+ plan->total_cost,
+ ((Scan *) plan)->scanrelid,
+ attnum);
+ }
+ scratch.opcode = EEOP_SCAN_VAR_TOAST;
+ }
+ else
+ scratch.opcode = EEOP_SCAN_VAR;
break;
}
}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 3c17cc6b1e..bd769fdeb6 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -57,6 +57,7 @@
#include "postgres.h"
#include "access/heaptoast.h"
+#include "access/detoast.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
#include "executor/execExpr.h"
@@ -157,6 +158,9 @@ static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
@@ -165,6 +169,9 @@ static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull
static Datum ExecJustInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
@@ -180,6 +187,43 @@ static pg_attribute_always_inline void ExecAggPlainTransByRef(AggState *aggstate
AggStatePerGroup pergroup,
ExprContext *aggcontext,
int setno);
+static inline void
+ExecSlotDetoastDatum(TupleTableSlot *slot, int attnum)
+{
+ if (!slot->tts_isnull[attnum] &&
+ VARATT_IS_EXTENDED(slot->tts_values[attnum]))
+ {
+ Datum oldDatum;
+ MemoryContext old = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ oldDatum = slot->tts_values[attnum];
+ slot->tts_values[attnum] = PointerGetDatum(detoast_attr(
+ (struct varlena *) oldDatum));
+ Assert(slot->tts_nvalid > attnum);
+ if (oldDatum != slot->tts_values[attnum])
+ slot->pre_detoasted_attrs = bms_add_member(slot->pre_detoasted_attrs, attnum);
+ MemoryContextSwitchTo(old);
+ }
+}
+
+/* JIT requires a non-static (and external?) function */
+void
+ExecSlotDetoastDatumExternal(TupleTableSlot *slot, int attnum)
+{
+ return ExecSlotDetoastDatum(slot, attnum);
+}
+
+
+static inline void
+ExecEvalToastVar(TupleTableSlot *slot,
+ ExprEvalStep *op,
+ int attnum)
+{
+ ExecSlotDetoastDatum(slot, attnum);
+
+ *op->resvalue = slot->tts_values[attnum];
+ *op->resnull = slot->tts_isnull[attnum];
+}
/*
* ScalarArrayOpExprHashEntry
@@ -295,6 +339,24 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustScanVar;
return;
}
+ if (step0 == EEOP_INNER_FETCHSOME &&
+ step1 == EEOP_INNER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustInnerVarToast;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_FETCHSOME &&
+ step1 == EEOP_OUTER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustOuterVarToast;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_FETCHSOME &&
+ step1 == EEOP_SCAN_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustScanVarToast;
+ return;
+ }
else if (step0 == EEOP_INNER_FETCHSOME &&
step1 == EEOP_ASSIGN_INNER_VAR)
{
@@ -330,6 +392,7 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustConst;
return;
}
+ /* ???? */
else if (step0 == EEOP_INNER_VAR)
{
state->evalfunc_private = (void *) ExecJustInnerVarVirt;
@@ -345,6 +408,21 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustScanVarVirt;
return;
}
+ else if (step0 == EEOP_INNER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustInnerVarVirtToast;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustOuterVarVirtToast;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustScanVarVirtToast;
+ return;
+ }
else if (step0 == EEOP_ASSIGN_INNER_VAR)
{
state->evalfunc_private = (void *) ExecJustAssignInnerVarVirt;
@@ -412,6 +490,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_INNER_VAR,
&&CASE_EEOP_OUTER_VAR,
&&CASE_EEOP_SCAN_VAR,
+ &&CASE_EEOP_INNER_VAR_TOAST,
+ &&CASE_EEOP_OUTER_VAR_TOAST,
+ &&CASE_EEOP_SCAN_VAR_TOAST,
&&CASE_EEOP_INNER_SYSVAR,
&&CASE_EEOP_OUTER_SYSVAR,
&&CASE_EEOP_SCAN_SYSVAR,
@@ -595,6 +676,25 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
*op->resvalue = scanslot->tts_values[attnum];
*op->resnull = scanslot->tts_isnull[attnum];
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_INNER_VAR_TOAST)
+ {
+ ExecEvalToastVar(innerslot, op, op->d.var.attnum);
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_OUTER_VAR_TOAST)
+ {
+ ExecEvalToastVar(outerslot, op, op->d.var.attnum);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SCAN_VAR_TOAST)
+ {
+ ExecEvalToastVar(scanslot, op, op->d.var.attnum);
EEO_NEXT();
}
@@ -2126,6 +2226,42 @@ ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
return ExecJustVarImpl(state, econtext->ecxt_scantuple, isnull);
}
+static pg_attribute_always_inline Datum
+ExecJustVarImplToast(ExprState *state, TupleTableSlot *slot, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum;
+
+ CheckOpSlotCompatibility(&state->steps[0], slot);
+
+ slot_getattr(slot, attnum + 1, isnull);
+
+ ExecSlotDetoastDatum(slot, attnum);
+
+ return slot->tts_values[attnum];
+}
+
+/* Simple reference to inner Var */
+static Datum
+ExecJustInnerVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_innertuple, isnull);
+}
+
+/* Simple reference to outer Var */
+static Datum
+ExecJustOuterVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_outertuple, isnull);
+}
+
+/* Simple reference to scan Var */
+static Datum
+ExecJustScanVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_scantuple, isnull);
+}
+
/* implementation of ExecJustAssign(Inner|Outer|Scan)Var */
static pg_attribute_always_inline Datum
ExecJustAssignVarImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
@@ -2264,6 +2400,51 @@ ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
return ExecJustVarVirtImpl(state, econtext->ecxt_scantuple, isnull);
}
+/* implementation of ExecJust(Inner|Outer|Scan)VarVirt */
+static pg_attribute_always_inline Datum
+ExecJustVarVirtImplToast(ExprState *state, TupleTableSlot *slot, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[0];
+ int attnum = op->d.var.attnum;
+
+ /*
+ * As it is guaranteed that a virtual slot is used, there never is a need
+ * to perform tuple deforming (nor would it be possible). Therefore
+ * execExpr.c has not emitted an EEOP_*_FETCHSOME step. Verify, as much as
+ * possible, that that determination was accurate.
+ */
+ Assert(TTS_IS_VIRTUAL(slot));
+ Assert(TTS_FIXED(slot));
+ Assert(attnum >= 0 && attnum < slot->tts_nvalid);
+
+ *isnull = slot->tts_isnull[attnum];
+
+ ExecSlotDetoastDatum(slot, attnum);
+
+ return slot->tts_values[attnum];
+}
+
+/* Like ExecJustInnerVar, optimized for virtual slots */
+static Datum
+ExecJustInnerVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_innertuple, isnull);
+}
+
+/* Like ExecJustOuterVar, optimized for virtual slots */
+static Datum
+ExecJustOuterVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_outertuple, isnull);
+}
+
+/* Like ExecJustScanVar, optimized for virtual slots */
+static Datum
+ExecJustScanVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_scantuple, isnull);
+}
+
/* implementation of ExecJustAssign(Inner|Outer|Scan)VarVirt */
static pg_attribute_always_inline Datum
ExecJustAssignVarVirtImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index a7aa2ee02b..08c96a42b1 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -79,6 +79,9 @@ static inline void tts_buffer_heap_store_tuple(TupleTableSlot *slot,
bool transfer_pin);
static void tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree);
+static Bitmapset *cal_final_pre_detoast_attrs(Bitmapset *plan_pre_detoast_attrs,
+ TupleDesc tupleDesc,
+ List *not_pre_detoast_vars);
const TupleTableSlotOps TTSOpsVirtual;
const TupleTableSlotOps TTSOpsHeapTuple;
@@ -176,6 +179,10 @@ tts_virtual_materialize(TupleTableSlot *slot)
if (att->attbyval || slot->tts_isnull[natt])
continue;
+ if (bms_is_member(natt, slot->pre_detoasted_attrs))
+ /* it has been in slot->tts_mcxt already. */
+ continue;
+
val = slot->tts_values[natt];
if (att->attlen == -1 &&
@@ -392,6 +399,13 @@ tts_heap_materialize(TupleTableSlot *slot)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_values is treated as non valid (tts_nvalid = 0), so let free the
+ * pre-detoast datum.
+ */
+ ExecFreePreDetoastDatum(slot);
+
}
static void
@@ -457,6 +471,9 @@ tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /* slot_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
@@ -567,6 +584,12 @@ tts_minimal_materialize(TupleTableSlot *slot)
mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_values is treated as non valid (tts_nvalid = 0), free the
+ * pre-detoast datum.
+ */
+ ExecFreePreDetoastDatum(slot);
}
static void
@@ -637,6 +660,9 @@ tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /* tts_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
@@ -771,6 +797,12 @@ tts_buffer_heap_materialize(TupleTableSlot *slot)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_nvalid = 0 means tts_values will be not reliable, so clear the
+ * information about pre-detoast-datum.
+ */
+ ExecFreePreDetoastDatum(slot);
}
static void
@@ -860,6 +892,7 @@ tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
if (TTS_SHOULDFREE(slot))
{
/* materialized slot shouldn't have a buffer to release */
@@ -904,6 +937,8 @@ tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
*/
ReleaseBuffer(buffer);
}
+
+ ExecFreePreDetoastDatum(slot);
}
/*
@@ -1138,6 +1173,7 @@ MakeTupleTableSlot(TupleDesc tupleDesc,
slot->tts_tupleDescriptor = tupleDesc;
slot->tts_mcxt = CurrentMemoryContext;
slot->tts_nvalid = 0;
+ slot->pre_detoasted_attrs = NULL;
if (tupleDesc != NULL)
{
@@ -1810,12 +1846,30 @@ void
ExecInitScanTupleSlot(EState *estate, ScanState *scanstate,
TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
{
+ Scan *splan = (Scan *) scanstate->ps.plan;
+
scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
tupledesc, tts_ops);
scanstate->ps.scandesc = tupledesc;
scanstate->ps.scanopsfixed = tupledesc != NULL;
scanstate->ps.scanops = tts_ops;
scanstate->ps.scanopsset = true;
+
+ if (is_scan_plan((Plan *) splan))
+ {
+ /*
+ * We may run detoast in Qual or Projection, but all of them happen at
+ * the ss_ScanTupleSlot rather than ps_ResultTupleSlot. So we can only
+ * take care of the ss_ScanTupleSlot.
+ *
+ * the ps_ResultTupleSlot may also have detoast on its parent node,
+ * like as a inner or outer slot in join case, the pre_detoast on
+ * these slot.
+ */
+ scanstate->scan_pre_detoast_attrs = cal_final_pre_detoast_attrs(splan->reference_attrs,
+ tupledesc,
+ splan->plan.forbid_pre_detoast_vars);
+ }
}
/* ----------------
@@ -2336,3 +2390,79 @@ end_tup_output(TupOutputState *tstate)
ExecDropSingleTupleTableSlot(tstate->slot);
pfree(tstate);
}
+
+static Bitmapset *
+cal_final_pre_detoast_attrs(Bitmapset *plan_pre_detoast_attrs,
+ TupleDesc tupleDesc,
+ List *not_pre_detoast_vars)
+{
+ Bitmapset *final = NULL,
+ *toast_attrs = NULL,
+ *forbid_pre_detoast_attrs = NULL;
+
+ int i;
+ ListCell *lc;
+
+ if (bms_is_empty(plan_pre_detoast_attrs))
+ return NULL;
+
+ /*
+ * there is no exact data type in create_plan or set_plan_refs stage, so
+ * plan_pre_detoast_attrs may have some attribute which is not toast attrs
+ * at all, which should be removed.
+ */
+ for (i = 0; i < tupleDesc->natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(tupleDesc, i);
+
+ if (attr->attlen == -1 && attr->attstorage != TYPSTORAGE_PLAIN)
+ toast_attrs = bms_add_member(toast_attrs, attr->attnum - 1);
+ }
+
+ final = bms_intersect(plan_pre_detoast_attrs, toast_attrs);
+
+ /*
+ * Due to the fact of detoast-datum will make the tuple bigger which is
+ * bad for some nodes like Sort/Hash, to avoid performance regression,
+ * such attribute should be removed as well.
+ */
+ foreach(lc, not_pre_detoast_vars)
+ {
+ Var *var = lfirst_node(Var, lc);
+
+ forbid_pre_detoast_attrs = bms_add_member(forbid_pre_detoast_attrs, var->varattno - 1);
+ }
+
+ final = bms_del_members(final, forbid_pre_detoast_attrs);
+
+ bms_free(toast_attrs);
+ bms_free(forbid_pre_detoast_attrs);
+
+ return final;
+}
+
+
+/* Input slot, result slot. scan slot? */
+void
+SetPredetoastAttrsForScan(ScanState *scanstate)
+{
+}
+
+void
+SetPredetoastAttrsForJoin(JoinState *j)
+{
+ PlanState *outerstate = outerPlanState(j);
+ PlanState *innerstate = innerPlanState(j);
+
+ /* Input slot, result slot. scan slot? */
+
+ j->outer_pre_detoast_attrs = cal_final_pre_detoast_attrs(
+ ((Join *) j->ps.plan)->outer_reference_attrs,
+ outerstate->ps_ResultTupleDesc,
+ outerstate->plan->forbid_pre_detoast_vars);
+
+ j->inner_pre_detoast_attrs = cal_final_pre_detoast_attrs(
+ ((Join *) j->ps.plan)->inner_reference_attrs,
+ innerstate->ps_ResultTupleDesc,
+ innerstate->plan->forbid_pre_detoast_vars);
+}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index cff5dc723e..43de95f5b0 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -572,6 +572,11 @@ ExecConditionalAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc,
planstate->resultopsset = planstate->scanopsset;
planstate->resultopsfixed = planstate->scanopsfixed;
planstate->resultops = planstate->scanops;
+
+ /*
+ * XXX: can I make sure the ps_ResultTupleDesc is set all the time?
+ */
+ Assert(planstate->ps_ResultTupleDesc != NULL);
}
else
{
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 1cbec4647c..19a05ed624 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -756,6 +756,8 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate, eflags);
innerDesc = ExecGetResultType(innerPlanState(hjstate));
+ SetPredetoastAttrsForJoin((JoinState *) hjstate);
+
/*
* Initialize result slot, type and projection.
*/
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index c1a8ca2464..be7cbd7f30 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -1497,6 +1497,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
(eflags | EXEC_FLAG_MARK));
innerDesc = ExecGetResultType(innerPlanState(mergestate));
+ SetPredetoastAttrsForJoin((JoinState *) mergestate);
+
/*
* For certain types of inner child nodes, it is advantageous to issue
* MARK every time we advance past an inner tuple we will never return to.
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 06fa0a9b31..2d40d19192 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -306,6 +306,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
*/
ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
+ SetPredetoastAttrsForJoin((JoinState *) nlstate);
/*
* initialize child expressions
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 33161d812f..56c10c9e4d 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -396,30 +396,52 @@ llvm_compile_expr(ExprState *state)
case EEOP_INNER_VAR:
case EEOP_OUTER_VAR:
case EEOP_SCAN_VAR:
+ case EEOP_INNER_VAR_TOAST:
+ case EEOP_OUTER_VAR_TOAST:
+ case EEOP_SCAN_VAR_TOAST:
{
LLVMValueRef value,
isnull;
LLVMValueRef v_attnum;
LLVMValueRef v_values;
LLVMValueRef v_nulls;
+ LLVMValueRef v_slot;
- if (opcode == EEOP_INNER_VAR)
+ if (opcode == EEOP_INNER_VAR || opcode == EEOP_INNER_VAR_TOAST)
{
+ v_slot = v_innerslot;
v_values = v_innervalues;
v_nulls = v_innernulls;
}
- else if (opcode == EEOP_OUTER_VAR)
+ else if (opcode == EEOP_OUTER_VAR || opcode == EEOP_OUTER_VAR_TOAST)
{
+ v_slot = v_outerslot;
v_values = v_outervalues;
v_nulls = v_outernulls;
}
else
{
+ v_slot = v_scanslot;
v_values = v_scanvalues;
v_nulls = v_scannulls;
}
v_attnum = l_int32_const(lc, op->d.var.attnum);
+
+ if (opcode == EEOP_INNER_VAR_TOAST ||
+ opcode == EEOP_OUTER_VAR_TOAST ||
+ opcode == EEOP_SCAN_VAR_TOAST)
+ {
+ LLVMValueRef params[2];
+
+ params[0] = v_slot;
+ params[1] = l_int32_const(lc, op->d.var.attnum);
+ l_call(b,
+ llvm_pg_var_func_type("ExecSlotDetoastDatumExternal"),
+ llvm_pg_func(mod, "ExecSlotDetoastDatumExternal"),
+ params, lengthof(params), "");
+ }
+
value = l_load_gep1(b, TypeSizeT, v_values, v_attnum, "");
isnull = l_load_gep1(b, TypeStorageBool, v_nulls, v_attnum, "");
LLVMBuildStore(b, value, v_resvaluep);
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 5212f529c8..11a11028b8 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -177,4 +177,5 @@ void *referenced_functions[] =
strlen,
varsize_any,
ExecInterpExprStillValid,
+ ExecSlotDetoastDatumExternal,
};
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index f4b61085be..e0eb150e5b 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -774,6 +774,19 @@ bms_membership(const Bitmapset *a)
* foo = bms_add_member(foo, x);
*/
+/*
+ * does this break commit 00b41463c21615f9bf3927f207e37f9e215d32e6?
+ * but I just found alloc memory and free the memory is too bad
+ * for this current feature. So let see ...;
+ */
+void
+bms_zero(Bitmapset *a)
+{
+ if (a == NULL)
+ return;
+
+ memset(a->words, 0, a->nwords * sizeof(bitmapword));
+}
/*
* bms_add_member - add a specified member to set
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ca619eab94..3f3aea73ce 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -42,6 +42,7 @@
#include "partitioning/partprune.h"
#include "utils/lsyscache.h"
+extern bool jit_enabled;
/*
* Flag bits that can appear in the flags argument of create_plan_recurse().
@@ -314,7 +315,8 @@ static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan,
List *mergeActionLists, int epqParam);
static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
GatherMergePath *best_path);
-
+static void set_plan_not_detoast_attrs_recurse(Plan *plan,
+ List *recheck_list);
/*
* create_plan
@@ -346,6 +348,8 @@ create_plan(PlannerInfo *root, Path *best_path)
/* Recursively process the path tree, demanding the correct tlist result */
plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST);
+ set_plan_not_detoast_attrs_recurse(plan, NIL);
+
/*
* Make sure the topmost plan node's targetlist exposes the original
* column names and other decorative info. Targetlists generated within
@@ -378,6 +382,68 @@ create_plan(PlannerInfo *root, Path *best_path)
return plan;
}
+/*
+ * set_plan_not_pre_detoast_vars
+ *
+ * set the toast_attrs according recheck_list.
+ *
+ * recheck_list = NIL means we need to do thing.
+ */
+static void
+set_plan_not_pre_detoast_vars(Plan *plan, List *recheck_list)
+{
+ ListCell *lc;
+ Var *var;
+
+ if (recheck_list == NIL)
+ return;
+
+ foreach(lc, plan->targetlist)
+ {
+ TargetEntry *te = lfirst_node(TargetEntry, lc);
+
+ if (!IsA(te->expr, Var))
+ continue;
+ var = castNode(Var, te->expr);
+ if (var->varattno <= 0)
+ continue;
+ if (list_member(recheck_list, var))
+ /* pass the recheck */
+ plan->forbid_pre_detoast_vars = lappend(plan->forbid_pre_detoast_vars, var);
+ }
+}
+
+
+static void
+set_plan_not_detoast_attrs_recurse(Plan *plan, List *recheck_list)
+{
+ if (plan == NULL)
+ return;
+
+ set_plan_not_pre_detoast_vars(plan, recheck_list);
+
+ if (IsA(plan, Sort) || IsA(plan, Memoize) || IsA(plan, WindowAgg) ||
+ IsA(plan, Hash) || IsA(plan, Material) || IsA(plan, IncrementalSort))
+ {
+ List *subplan_exprs = get_tlist_exprs(plan->lefttree->targetlist, true);
+
+ set_plan_not_pre_detoast_vars(plan, subplan_exprs);
+ set_plan_not_detoast_attrs_recurse(plan->lefttree, subplan_exprs);
+ }
+ else if (IsA(plan, HashJoin) && castNode(HashJoin, plan)->left_small_tlist)
+ {
+ List *subplan_exprs = get_tlist_exprs(plan->lefttree->targetlist, true);
+
+ set_plan_not_detoast_attrs_recurse(plan->lefttree, subplan_exprs);
+ set_plan_not_detoast_attrs_recurse(plan->righttree, plan->forbid_pre_detoast_vars);
+ }
+ else
+ {
+ set_plan_not_detoast_attrs_recurse(plan->lefttree, plan->forbid_pre_detoast_vars);
+ set_plan_not_detoast_attrs_recurse(plan->righttree, plan->forbid_pre_detoast_vars);
+ }
+}
+
/*
* create_plan_recurse
* Recursive guts of create_plan().
@@ -4884,6 +4950,11 @@ create_hashjoin_plan(PlannerInfo *root,
copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
+ join_plan->left_small_tlist = (best_path->num_batches > 1);
+
+ if (!jit_enabled)
+ elog(INFO, "num_batches = %d", best_path->num_batches);
+
return join_plan;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 22a1fa29f3..375e176788 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -27,6 +27,7 @@
#include "optimizer/tlist.h"
#include "parser/parse_relation.h"
#include "tcop/utility.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -55,11 +56,27 @@ typedef struct
tlist_vinfo vars[FLEXIBLE_ARRAY_MEMBER]; /* has num_vars entries */
} indexed_tlist;
+typedef struct
+{
+ /* var is added into existing_attrs for the first time. */
+ Bitmapset *existing_attrs;
+ /* following add to the final_ref_attrs. */
+ Bitmapset **final_ref_attrs;
+} intermediate_var_ref_context;
+
+typedef struct
+{
+ int level_added;
+ int level;
+} intermediate_level_context;
+
typedef struct
{
PlannerInfo *root;
int rtoffset;
double num_exec;
+ intermediate_level_context level_ctx;
+ intermediate_var_ref_context scan_reference_attrs;
} fix_scan_expr_context;
typedef struct
@@ -71,6 +88,9 @@ typedef struct
int rtoffset;
NullingRelsMatch nrm_match;
double num_exec;
+ intermediate_level_context level_ctx;
+ intermediate_var_ref_context outer_reference_attrs;
+ intermediate_var_ref_context inner_reference_attrs;
} fix_join_expr_context;
typedef struct
@@ -127,8 +147,8 @@ typedef struct
(((con)->consttype == REGCLASSOID || (con)->consttype == OIDOID) && \
!(con)->constisnull)
-#define fix_scan_list(root, lst, rtoffset, num_exec) \
- ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec))
+#define fix_scan_list(root, lst, rtoffset, num_exec, pre_detoast_attrs) \
+ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec, pre_detoast_attrs))
static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing);
static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
@@ -158,7 +178,8 @@ static Plan *set_mergeappend_references(PlannerInfo *root,
static void set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset);
static Relids offset_relid_set(Relids relids, int rtoffset);
static Node *fix_scan_expr(PlannerInfo *root, Node *node,
- int rtoffset, double num_exec);
+ int rtoffset, double num_exec,
+ Bitmapset **scan_reference_attrs);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
@@ -190,7 +211,10 @@ static List *fix_join_expr(PlannerInfo *root,
Index acceptable_rel,
int rtoffset,
NullingRelsMatch nrm_match,
- double num_exec);
+ double num_exec,
+ Bitmapset **outer_reference_attrs,
+ Bitmapset **inner_reference_attrs);
+
static Node *fix_join_expr_mutator(Node *node,
fix_join_expr_context *context);
static Node *fix_upper_expr(PlannerInfo *root,
@@ -628,10 +652,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_SampleScan:
@@ -641,13 +671,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs
+ );
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tablesample = (TableSampleClause *)
fix_scan_expr(root, (Node *) splan->tablesample,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_IndexScan:
@@ -657,28 +694,40 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
+
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
splan->indexqual =
fix_scan_list(root, splan->indexqual,
- rtoffset, 1);
+ rtoffset, 1, &splan->scan.reference_attrs);
splan->indexqualorig =
fix_scan_list(root, splan->indexqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->indexorderby =
fix_scan_list(root, splan->indexorderby,
- rtoffset, 1);
+ rtoffset, 1, &splan->scan.reference_attrs);
splan->indexorderbyorig =
fix_scan_list(root, splan->indexorderbyorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan), &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_IndexOnlyScan:
{
IndexOnlyScan *splan = (IndexOnlyScan *) plan;
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
+
return set_indexonlyscan_references(root, splan, rtoffset);
}
break;
@@ -691,10 +740,15 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->scan.plan.targetlist == NIL);
Assert(splan->scan.plan.qual == NIL);
splan->indexqual =
- fix_scan_list(root, splan->indexqual, rtoffset, 1);
+ fix_scan_list(root, splan->indexqual, rtoffset, 1,
+ &splan->scan.reference_attrs);
splan->indexqualorig =
fix_scan_list(root, splan->indexqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_BitmapHeapScan:
@@ -704,13 +758,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->bitmapqualorig =
fix_scan_list(root, splan->bitmapqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_TidScan:
@@ -720,13 +781,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tidquals =
fix_scan_list(root, splan->tidquals,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_TidRangeScan:
@@ -736,17 +804,25 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tidrangequals =
fix_scan_list(root, splan->tidrangequals,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_SubqueryScan:
/* Needs special treatment, see comments below */
+ /* XXX: shall I do anything? */
return set_subqueryscan_references(root,
(SubqueryScan *) plan,
rtoffset);
@@ -757,12 +833,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->functions =
- fix_scan_list(root, splan->functions, rtoffset, 1);
+ fix_scan_list(root, splan->functions, rtoffset, 1,
+ &splan->scan.reference_attrs);
+
}
break;
case T_TableFuncScan:
@@ -772,13 +852,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
splan->tablefunc = (TableFunc *)
fix_scan_expr(root, (Node *) splan->tablefunc,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
}
break;
case T_ValuesScan:
@@ -788,13 +872,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->values_lists =
fix_scan_list(root, splan->values_lists,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
}
break;
case T_CteScan:
@@ -804,10 +891,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_NamedTuplestoreScan:
@@ -817,10 +910,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
}
break;
case T_WorkTableScan:
@@ -830,10 +925,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
}
break;
case T_ForeignScan:
@@ -873,7 +970,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
mplan->param_exprs = fix_scan_list(root, mplan->param_exprs,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan),
+ NULL);
break;
}
@@ -933,9 +1031,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->plan.qual == NIL);
splan->limitOffset =
- fix_scan_expr(root, splan->limitOffset, rtoffset, 1);
+ fix_scan_expr(root, splan->limitOffset, rtoffset, 1, NULL);
splan->limitCount =
- fix_scan_expr(root, splan->limitCount, rtoffset, 1);
+ fix_scan_expr(root, splan->limitCount, rtoffset, 1, NULL);
}
break;
case T_Agg:
@@ -988,17 +1086,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
* variable refs, so fix_scan_expr works for them.
*/
wplan->startOffset =
- fix_scan_expr(root, wplan->startOffset, rtoffset, 1);
+ fix_scan_expr(root, wplan->startOffset, rtoffset, 1, NULL);
wplan->endOffset =
- fix_scan_expr(root, wplan->endOffset, rtoffset, 1);
+ fix_scan_expr(root, wplan->endOffset, rtoffset, 1, NULL);
wplan->runCondition = fix_scan_list(root,
wplan->runCondition,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan), NULL);
wplan->runConditionOrig = fix_scan_list(root,
wplan->runConditionOrig,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_Result:
@@ -1038,14 +1136,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->plan.targetlist =
fix_scan_list(root, splan->plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
splan->plan.qual =
fix_scan_list(root, splan->plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan), NULL);
}
/* resconstantqual can't contain any subplan variable refs */
splan->resconstantqual =
- fix_scan_expr(root, splan->resconstantqual, rtoffset, 1);
+ fix_scan_expr(root, splan->resconstantqual, rtoffset, 1, NULL);
}
break;
case T_ProjectSet:
@@ -1061,7 +1159,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->withCheckOptionLists =
fix_scan_list(root, splan->withCheckOptionLists,
- rtoffset, 1);
+ rtoffset, 1, NULL);
if (splan->returningLists)
{
@@ -1118,18 +1216,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
fix_join_expr(root, splan->onConflictSet,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan),
+ NULL, NULL);
splan->onConflictWhere = (Node *)
fix_join_expr(root, (List *) splan->onConflictWhere,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan),
+ NULL, NULL);
pfree(itlist);
splan->exclRelTlist =
- fix_scan_list(root, splan->exclRelTlist, rtoffset, 1);
+ fix_scan_list(root, splan->exclRelTlist, rtoffset, 1, NULL);
}
/*
@@ -1182,7 +1282,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
resultrel,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan),
+ NULL, NULL);
/* Fix quals too. */
action->qual = (Node *) fix_join_expr(root,
@@ -1191,7 +1292,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
resultrel,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL(plan));
+ NUM_EXEC_QUAL(plan),
+ NULL, NULL);
}
}
}
@@ -1356,13 +1458,16 @@ set_indexonlyscan_references(PlannerInfo *root,
NUM_EXEC_QUAL((Plan *) plan));
/* indexqual is already transformed to reference index columns */
plan->indexqual = fix_scan_list(root, plan->indexqual,
- rtoffset, 1);
+ rtoffset, 1,
+ &plan->scan.reference_attrs);
/* indexorderby is already transformed to reference index columns */
plan->indexorderby = fix_scan_list(root, plan->indexorderby,
- rtoffset, 1);
+ rtoffset, 1,
+ &plan->scan.reference_attrs);
/* indextlist must NOT be transformed to reference index columns */
plan->indextlist = fix_scan_list(root, plan->indextlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) plan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan),
+ &plan->scan.reference_attrs);
pfree(index_itlist);
@@ -1409,10 +1514,10 @@ set_subqueryscan_references(PlannerInfo *root,
plan->scan.scanrelid += rtoffset;
plan->scan.plan.targetlist =
fix_scan_list(root, plan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) plan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan), NULL);
plan->scan.plan.qual =
fix_scan_list(root, plan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) plan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) plan), NULL);
result = (Plan *) plan;
}
@@ -1612,7 +1717,7 @@ set_foreignscan_references(PlannerInfo *root,
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
fscan->fdw_scan_tlist =
fix_scan_list(root, fscan->fdw_scan_tlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan), NULL);
}
else
{
@@ -1622,16 +1727,16 @@ set_foreignscan_references(PlannerInfo *root,
*/
fscan->scan.plan.targetlist =
fix_scan_list(root, fscan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan), NULL);
fscan->scan.plan.qual =
fix_scan_list(root, fscan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
fscan->fdw_exprs =
fix_scan_list(root, fscan->fdw_exprs,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
fscan->fdw_recheck_quals =
fix_scan_list(root, fscan->fdw_recheck_quals,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
}
fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset);
@@ -1690,20 +1795,20 @@ set_customscan_references(PlannerInfo *root,
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
cscan->custom_scan_tlist =
fix_scan_list(root, cscan->custom_scan_tlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan), NULL);
}
else
{
/* Adjust tlist, qual, custom_exprs in the standard way */
cscan->scan.plan.targetlist =
fix_scan_list(root, cscan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan), NULL);
cscan->scan.plan.qual =
fix_scan_list(root, cscan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan), NULL);
cscan->custom_exprs =
fix_scan_list(root, cscan->custom_exprs,
- rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan), NULL);
}
/* Adjust child plan-nodes recursively, if needed */
@@ -2111,6 +2216,95 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
return (Node *) bestplan;
}
+
+static inline void
+setup_intermediate_level_ctx(intermediate_level_context * ctx)
+{
+ ctx->level = 0;
+ ctx->level_added = false;
+}
+
+static inline void
+setup_intermediate_var_ref_ctx(intermediate_var_ref_context * ctx, Bitmapset **final_ref_attrs)
+{
+ ctx->existing_attrs = NULL;
+ ctx->final_ref_attrs = final_ref_attrs;
+}
+
+/*
+ * increase_level_for_pre_detoast
+ * Check if the given Expr could detoast a Var directly, if yes,
+ * increase the level and return true. otherwise return false;
+ */
+static inline void
+increase_level_for_pre_detoast(Node *node, intermediate_level_context * ctx)
+{
+ /* The following nodes is impossible to detoast a Var directly. */
+ if (IsA(node, List) || IsA(node, TargetEntry) || IsA(node, NullTest))
+ {
+ ctx->level_added = false;
+ }
+ else if (IsA(node, FuncExpr) && castNode(FuncExpr, node)->funcid == F_PG_COLUMN_COMPRESSION)
+ {
+ /* let's not detoast first so that pg_column_compression works. */
+ ctx->level_added = false;
+ }
+ else
+ {
+ ctx->level_added = true;
+ ctx->level += 1;
+ }
+}
+
+static inline void
+decreased_level_for_pre_detoast(intermediate_level_context * ctx)
+{
+ if (ctx->level_added)
+ ctx->level -= 1;
+
+ ctx->level_added = false;
+}
+
+/*
+ * add_pre_detoast_vars
+ * add the var's information into pre_detoast_attrs when the check is pass.
+ */
+static inline void
+add_pre_detoast_vars(intermediate_level_context * level_ctx,
+ intermediate_var_ref_context * ctx,
+ Var *var)
+{
+ int attno;
+
+ if (level_ctx->level <= 1 || ctx->final_ref_attrs == NULL || var->varattno <= 0)
+ return;
+
+ attno = var->varattno - 1;
+ if (bms_is_member(attno, ctx->existing_attrs))
+ {
+ /* not the first time to access it, add it to final result. */
+ *ctx->final_ref_attrs = bms_add_member(*ctx->final_ref_attrs, attno);
+ }
+ else
+ {
+ /* first time. */
+ ctx->existing_attrs = bms_add_member(ctx->existing_attrs, attno);
+
+ /*
+ * XXX:
+ *
+ * The above strategy doesn't help to detect if a Var is detoast
+ * twice. Reasons are: 1. the context is not maintain in Plan node
+ * level. so if it is detoast at targetlist and qual, we can't detect
+ * it. 2. even we can make it at plan node, it still doesn't help for
+ * the among-nodes case.
+ *
+ * So for now, I just disable it.
+ */
+ *ctx->final_ref_attrs = bms_add_member(*ctx->final_ref_attrs, attno);
+ }
+}
+
/*
* fix_scan_expr
* Do set_plan_references processing on a scan-level expression
@@ -2130,13 +2324,16 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
* if that seems safe.
*/
static Node *
-fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
+fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset,
+ double num_exec, Bitmapset **scan_reference_attrs)
{
fix_scan_expr_context context;
context.root = root;
context.rtoffset = rtoffset;
context.num_exec = num_exec;
+ setup_intermediate_level_ctx(&context.level_ctx);
+ setup_intermediate_var_ref_ctx(&context.scan_reference_attrs, scan_reference_attrs);
if (rtoffset != 0 ||
root->multiexpr_params != NIL ||
@@ -2167,8 +2364,13 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
static Node *
fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
{
+ Node *n;
+
if (node == NULL)
return NULL;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
if (IsA(node, Var))
{
Var *var = copyVar((Var *) node);
@@ -2186,10 +2388,18 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
var->varno += context->rtoffset;
if (var->varnosyn > 0)
var->varnosyn += context->rtoffset;
+
+ add_pre_detoast_vars(&context->level_ctx, &context->scan_reference_attrs, var);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) var;
}
if (IsA(node, Param))
- return fix_param_node(context->root, (Param *) node);
+ {
+ Node *n = fix_param_node(context->root, (Param *) node);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
+ }
if (IsA(node, Aggref))
{
Aggref *aggref = (Aggref *) node;
@@ -2200,7 +2410,10 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
if (aggparam != NULL)
{
/* Make a copy of the Param for paranoia's sake */
- return (Node *) copyObject(aggparam);
+ Node *n = (Node *) copyObject(aggparam);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
}
/* If no match, just fall through to process it normally */
}
@@ -2210,6 +2423,7 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
Assert(!IS_SPECIAL_VARNO(cexpr->cvarno));
cexpr->cvarno += context->rtoffset;
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) cexpr;
}
if (IsA(node, PlaceHolderVar))
@@ -2218,29 +2432,52 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
PlaceHolderVar *phv = (PlaceHolderVar *) node;
/* XXX can we assert something about phnullingrels? */
- return fix_scan_expr_mutator((Node *) phv->phexpr, context);
+ Node *n = fix_scan_expr_mutator((Node *) phv->phexpr, context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
}
if (IsA(node, AlternativeSubPlan))
- return fix_scan_expr_mutator(fix_alternative_subplan(context->root,
- (AlternativeSubPlan *) node,
- context->num_exec),
- context);
+ {
+ Node *n = fix_scan_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
+ }
fix_expr_common(context->root, node);
- return expression_tree_mutator(node, fix_scan_expr_mutator,
- (void *) context);
+ n = expression_tree_mutator(node, fix_scan_expr_mutator, (void *) context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
}
static bool
fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
{
+ bool ret;
+
if (node == NULL)
return false;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
+ if (IsA(node, Var))
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->scan_reference_attrs,
+ castNode(Var, node));
+ }
Assert(!(IsA(node, Var) && ((Var *) node)->varno == ROWID_VAR));
Assert(!IsA(node, PlaceHolderVar));
Assert(!IsA(node, AlternativeSubPlan));
fix_expr_common(context->root, node);
- return expression_tree_walker(node, fix_scan_expr_walker,
- (void *) context);
+ ret = expression_tree_walker(node, fix_scan_expr_walker,
+ (void *) context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret;
}
/*
@@ -2276,7 +2513,10 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs
+ );
/* Now do join-type-specific stuff */
if (IsA(join, NestLoop))
@@ -2323,7 +2563,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
}
else if (IsA(join, HashJoin))
{
@@ -2336,7 +2578,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
/*
* HashJoin's hashkeys are used to look for matching tuples from its
@@ -2368,7 +2612,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
(join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
- NUM_EXEC_TLIST((Plan *) join));
+ NUM_EXEC_TLIST((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
join->plan.qual = fix_join_expr(root,
join->plan.qual,
outer_itlist,
@@ -2376,8 +2622,20 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
(join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
- NUM_EXEC_QUAL((Plan *) join));
-
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
+
+ join->plan.forbid_pre_detoast_vars = fix_join_expr(root,
+ join->plan.forbid_pre_detoast_vars,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset,
+ (join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
+ NUM_EXEC_TLIST((Plan *) join),
+ NULL,
+ NULL);
pfree(outer_itlist);
pfree(inner_itlist);
}
@@ -3010,9 +3268,12 @@ fix_join_expr(PlannerInfo *root,
Index acceptable_rel,
int rtoffset,
NullingRelsMatch nrm_match,
- double num_exec)
+ double num_exec,
+ Bitmapset **outer_reference_attrs,
+ Bitmapset **inner_reference_attrs)
{
fix_join_expr_context context;
+ List *ret;
context.root = root;
context.outer_itlist = outer_itlist;
@@ -3021,16 +3282,30 @@ fix_join_expr(PlannerInfo *root,
context.rtoffset = rtoffset;
context.nrm_match = nrm_match;
context.num_exec = num_exec;
- return (List *) fix_join_expr_mutator((Node *) clauses, &context);
+
+ setup_intermediate_level_ctx(&context.level_ctx);
+ setup_intermediate_var_ref_ctx(&context.outer_reference_attrs, outer_reference_attrs);
+ setup_intermediate_var_ref_ctx(&context.inner_reference_attrs, inner_reference_attrs);
+
+ ret = (List *) fix_join_expr_mutator((Node *) clauses, &context);
+
+ bms_free(context.outer_reference_attrs.existing_attrs);
+ bms_free(context.inner_reference_attrs.existing_attrs);
+
+ return ret;
}
static Node *
fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
{
Var *newvar;
+ Node *ret_node;
if (node == NULL)
return NULL;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
if (IsA(node, Var))
{
Var *var = (Var *) node;
@@ -3044,7 +3319,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->rtoffset,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* then in the inner. */
@@ -3056,7 +3337,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->rtoffset,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* If it's for acceptable_rel, adjust and return it */
@@ -3066,6 +3353,9 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
var->varno += context->rtoffset;
if (var->varnosyn > 0)
var->varnosyn += context->rtoffset;
+ /* XXX acceptable_rel? we can ignore it for safety. */
+ decreased_level_for_pre_detoast(&context->level_ctx);
+
return (Node *) var;
}
@@ -3084,22 +3374,38 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
OUTER_VAR,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
if (context->inner_itlist && context->inner_itlist->has_ph_vars)
{
+
newvar = search_indexed_tlist_for_phv(phv,
context->inner_itlist,
INNER_VAR,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* If not supplied by input plans, evaluate the contained expr */
/* XXX can we assert something about phnullingrels? */
- return fix_join_expr_mutator((Node *) phv->phexpr, context);
+ ret_node = fix_join_expr_mutator((Node *) phv->phexpr, context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
}
+
/* Try matching more complex expressions too, if tlists have any */
if (context->outer_itlist && context->outer_itlist->has_non_vars)
{
@@ -3107,7 +3413,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->outer_itlist,
OUTER_VAR);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
if (context->inner_itlist && context->inner_itlist->has_non_vars)
{
@@ -3115,20 +3427,36 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->inner_itlist,
INNER_VAR);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* Special cases (apply only AFTER failing to match to lower tlist) */
if (IsA(node, Param))
- return fix_param_node(context->root, (Param *) node);
+ {
+ ret_node = fix_param_node(context->root, (Param *) node);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
+ }
if (IsA(node, AlternativeSubPlan))
- return fix_join_expr_mutator(fix_alternative_subplan(context->root,
- (AlternativeSubPlan *) node,
- context->num_exec),
- context);
+ {
+ ret_node = fix_join_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
+ }
fix_expr_common(context->root, node);
- return expression_tree_mutator(node,
- fix_join_expr_mutator,
- (void *) context);
+ ret_node = expression_tree_mutator(node,
+ fix_join_expr_mutator,
+ (void *) context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
}
/*
@@ -3163,7 +3491,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
* varno = newvarno, varattno = resno of corresponding targetlist element.
* The original tree is not modified.
*/
-static Node *
+static Node * /* XXX: shall I care about this for shared
+ * detoast optimization? */
fix_upper_expr(PlannerInfo *root,
Node *node,
indexed_tlist *subplan_itlist,
@@ -3318,7 +3647,10 @@ set_returning_clause_references(PlannerInfo *root,
resultRelation,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_TLIST(topplan));
+ NUM_EXEC_TLIST(topplan),
+ NULL,
+ NULL
+ );
pfree(itlist);
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index a20c539a25..9a31e083dd 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -77,6 +77,11 @@ typedef enum ExprEvalOp
EEOP_OUTER_VAR,
EEOP_SCAN_VAR,
+ /* compute non-system Var value with shared-detoast-datum logic */
+ EEOP_INNER_VAR_TOAST,
+ EEOP_OUTER_VAR_TOAST,
+ EEOP_SCAN_VAR_TOAST,
+
/* compute system Var value */
EEOP_INNER_SYSVAR,
EEOP_OUTER_SYSVAR,
@@ -826,5 +831,6 @@ extern void ExecEvalAggOrderedTransDatum(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecSlotDetoastDatumExternal(TupleTableSlot *slot, int attnum);
#endif /* EXEC_EXPR_H */
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 6133dbcd0a..5b42eb65f8 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,6 +18,7 @@
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/tupdesc.h"
+#include "nodes/bitmapset.h"
#include "storage/buf.h"
/*----------
@@ -128,6 +129,11 @@ typedef struct TupleTableSlot
MemoryContext tts_mcxt; /* slot itself is in this context */
ItemPointerData tts_tid; /* stored tuple's tid */
Oid tts_tableOid; /* table oid of tuple */
+
+ /*
+ * The attributes populated by EEOP_{INNER/OUTER/SCAN}_VAR_TOAST step.
+ */
+ Bitmapset *pre_detoasted_attrs;
} TupleTableSlot;
/* routines for a TupleTableSlot implementation */
@@ -426,12 +432,38 @@ slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
return slot->tts_ops->getsysattr(slot, attnum, isnull);
}
+static inline void
+ExecFreePreDetoastDatum(TupleTableSlot *slot)
+{
+ int attnum;
+
+ if (bms_is_empty(slot->pre_detoasted_attrs))
+ return;
+
+ attnum = -1;
+ /* free the memory used by pre-detoasted datum and reset the flags. */
+ while ((attnum = bms_next_member(slot->pre_detoasted_attrs, attnum)) >= 0)
+ {
+ pfree((void *) slot->tts_values[attnum]);
+ }
+
+ /*
+ * bms_free each time cost too much, so just zero these bits and keep its
+ * memory, just like what we did for TupleTableSlot. but.. see the
+ * comments about the bms_zero.
+ */
+ bms_zero(slot->pre_detoasted_attrs);
+}
+
+
/*
* ExecClearTuple - clear the slot's contents
*/
static inline TupleTableSlot *
ExecClearTuple(TupleTableSlot *slot)
{
+ ExecFreePreDetoastDatum(slot);
+
slot->tts_ops->clear(slot);
return slot;
@@ -450,6 +482,10 @@ ExecClearTuple(TupleTableSlot *slot)
static inline void
ExecMaterializeSlot(TupleTableSlot *slot)
{
+ /*
+ * XXX: pre_detoasted_attrs doesn't dependent on any external storage, so
+ * nothing should be done here.
+ */
slot->tts_ops->materialize(slot);
}
@@ -494,6 +530,30 @@ ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
dstslot->tts_ops->copyslot(dstslot, srcslot);
+ /* Assert this assumption the below code depends on. */
+ Assert(dstslot->tts_nvalid == 0 ||
+ dstslot->tts_nvalid == srcslot->tts_nvalid);
+
+ if (dstslot->tts_nvalid == srcslot->tts_nvalid
+ && !bms_is_empty(srcslot->pre_detoasted_attrs))
+ {
+ int attnum = -1;
+ MemoryContext old = MemoryContextSwitchTo(dstslot->tts_mcxt);
+
+ dstslot->pre_detoasted_attrs = bms_copy(srcslot->pre_detoasted_attrs);
+
+ while ((attnum = bms_next_member(dstslot->pre_detoasted_attrs, attnum)) >= 0)
+ {
+ struct varlena *datum = (struct varlena *) srcslot->tts_values[attnum];
+ Size len;
+
+ Assert(!VARATT_IS_EXTENDED(datum));
+ len = VARSIZE(datum);
+ dstslot->tts_values[attnum] = (Datum) palloc(len);
+ memcpy((void *) dstslot->tts_values[attnum], datum, len);
+ }
+ MemoryContextSwitchTo(old);
+ }
return dstslot;
}
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index efc8890ce6..e91f334254 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -113,6 +113,7 @@ extern Bitmapset *bms_add_range(Bitmapset *a, int lower, int upper);
extern Bitmapset *bms_int_members(Bitmapset *a, const Bitmapset *b);
extern Bitmapset *bms_del_members(Bitmapset *a, const Bitmapset *b);
extern Bitmapset *bms_join(Bitmapset *a, Bitmapset *b);
+extern void bms_zero(Bitmapset *a);
/* support for iterating through the integer elements of a set: */
extern int bms_next_member(const Bitmapset *a, int prevbit);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 561fdd98f1..1a01d480ce 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1474,6 +1474,7 @@ typedef struct ScanState
Relation ss_currentRelation;
struct TableScanDescData *ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
+ Bitmapset *scan_pre_detoast_attrs;
} ScanState;
/* ----------------
@@ -2003,6 +2004,8 @@ typedef struct JoinState
bool single_match; /* True if we should skip to next outer tuple
* after finding one inner match */
ExprState *joinqual; /* JOIN quals (in addition to ps.qual) */
+ Bitmapset *outer_pre_detoast_attrs;
+ Bitmapset *inner_pre_detoast_attrs;
} JoinState;
/* ----------------
@@ -2764,4 +2767,6 @@ typedef struct LimitState
TupleTableSlot *last_slot; /* slot for evaluation of ties */
} LimitState;
+extern void SetPredetoastAttrsForJoin(JoinState *joinstate);
+extern void SetPredetoastAttrsForScan(ScanState *scanstate);
#endif /* EXECNODES_H */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index b4ef6bc44c..2f2fec2740 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -169,6 +169,13 @@ typedef struct Plan
*/
Bitmapset *extParam;
Bitmapset *allParam;
+
+ /*
+ * A list of Vars which should not apply the shared-detoast-datum logic
+ * since the upper nodes like Sort/Hash want the tuples as small as
+ * possible. Its a subset of targetlist in each Plan node.
+ */
+ List *forbid_pre_detoast_vars;
} Plan;
/* ----------------
@@ -385,6 +392,13 @@ typedef struct Scan
Plan plan;
Index scanrelid; /* relid is index into the range table */
+
+ /*
+ * Records of var's varattno - 1 where the Var is accessed indirectly by
+ * any expression, like a > 3. However a IS [NOT] NULL is not included
+ * since it doesn't access the tts_values[*] at all.
+ */
+ Bitmapset *reference_attrs;
} Scan;
/* ----------------
@@ -789,6 +803,14 @@ typedef struct Join
JoinType jointype;
bool inner_unique;
List *joinqual; /* JOIN quals (in addition to plan.qual) */
+
+ /*
+ * Records of var's varattno - 1 where the Var is accessed indirectly by
+ * any expression, like a > 3. However a IS [NOT] NULL is not included
+ * since it doesn't access the tts_values[*] at all.
+ */
+ Bitmapset *outer_reference_attrs;
+ Bitmapset *inner_reference_attrs;
} Join;
/* ----------------
@@ -869,6 +891,14 @@ typedef struct HashJoin
* perform lookups in the hashtable over the inner plan.
*/
List *hashkeys;
+
+ /*
+ * keep the small tlist information in plan for the shared-detoast datum
+ * logic. If left_small_tlist is true, then all the datum in outerPlan
+ * should not apply that logic, used for maintaining
+ * forbid_pre_detoast_vars fields in Plan.
+ */
+ bool left_small_tlist;
} HashJoin;
/* ----------------
@@ -1588,4 +1618,24 @@ typedef enum MonotonicFunction
MONOTONICFUNC_BOTH = MONOTONICFUNC_INCREASING | MONOTONICFUNC_DECREASING,
} MonotonicFunction;
+static inline bool
+is_join_plan(Plan *plan)
+{
+ return (plan != NULL) && (IsA(plan, NestLoop) || IsA(plan, HashJoin) || IsA(plan, MergeJoin));
+}
+
+static inline bool
+is_scan_plan(Plan *plan)
+{
+ return (plan != NULL) &&
+ (IsA(plan, SeqScan) ||
+ IsA(plan, SampleScan) ||
+ IsA(plan, IndexScan) ||
+ IsA(plan, IndexOnlyScan) ||
+ IsA(plan, BitmapIndexScan) ||
+ IsA(plan, BitmapHeapScan) ||
+ IsA(plan, TidScan) ||
+ IsA(plan, SubqueryScan));
+}
+
#endif /* PLANNODES_H */
diff --git a/src/test/regress/sql/shared_detoast_slow.sql b/src/test/regress/sql/shared_detoast_slow.sql
new file mode 100644
index 0000000000..beecaa6bc5
--- /dev/null
+++ b/src/test/regress/sql/shared_detoast_slow.sql
@@ -0,0 +1,70 @@
+create table t1(a text, b text, c text);
+create table t2(a text, b text, c text);
+create table t3(a text, b text, c text);
+
+insert into t1 select i, i, i from generate_series(1, 1000000)i;
+insert into t2 select i, i, i from generate_series(1, 1000000)i;
+insert into t3 select i, i, i from generate_series(1, 1000000)i;
+
+create index on t1(c);
+
+analyze t1;
+analyze t2;
+analyze t3;
+
+-- Turn off jit first, reasons:
+-- 1. JIT is not adapted for this feature, it may cause crash on jit.
+-- 2. more logging for this feature is enabled when jit=off
+set jit to off;
+
+explain (verbose) select * from t1 where b > 'a';
+
+
+-- NullTest has nothing with tts_values, so its access to toast value
+-- should be ignored.
+explain (verbose) select * from t1 where b is NULL and c is not null;
+
+-- b can't be shared-detoasted since it would make the work_mem bigger.
+explain (verbose) select * from t1 where b > 'a' order by c;
+
+-- b CAN be shared-detoasted since it would NOT make the work_mem bigger.
+-- but compared with the old behavior, it cause the lifespan of the
+-- 'detoast datum'
+-- longer, in the old behavior, it is reset becase of ExecQualAndReset.
+explain (verbose) select a, c from t1 where b > 'a' order by c;
+
+-- The detoast only happen at the join stage.
+explain (verbose) select * from t1 join t2 using(b);
+
+--
+explain (verbose) select * from t1 join t2 using(b) where t1.c > '3';
+
+explain (verbose)
+select t3.*
+from t1, t2, t3
+where t2.c > '999999999999999'
+and t2.c = t1.c
+and t3.b = t1.b;
+
+
+-- t2.b the innerHash can't be pre detoast due to Hash.
+-- t1.b the outerPlan can't be pre detoast due to num_batch.
+explain (verbose) select * from t1 join t2 using(b);
+
+-- Increase the work_mem so that the num_batch = 1, then
+-- the t1.b in outerPlan can be pre-detoast.
+
+set work_mem to '1GB';
+explain (verbose) select * from t1 join t2 using(b);
+reset work_mem;
+
+-- Show even if a datum is under a hash node, but it is
+-- not DIRECTLY input of the Hash, it's still able to be
+-- pre detoasted.
+explain (verbose)
+select t3.*
+from t1, t2, t3
+where t2.c > '999999999999999'
+and t2.c = t1.c
+and t3.b = t1.b
+and t1.b > '0';
--
2.34.1
^ permalink raw reply [nested|flat] 11+ messages in thread
* Re: Shared detoast Datum proposal
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-06 15:12 ` Re: Shared detoast Datum proposal vignesh C <[email protected]>
2024-01-07 01:10 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
@ 2024-01-08 09:52 ` Andy Fan <[email protected]>
2024-01-22 06:41 ` Re: Shared detoast Datum proposal Peter Smith <[email protected]>
0 siblings, 1 reply; 11+ messages in thread
From: Andy Fan @ 2024-01-08 09:52 UTC (permalink / raw)
To: Andy Fan <[email protected]>; +Cc: vignesh C <[email protected]>; [email protected] <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>
Andy Fan <[email protected]> writes:
>>
>> One of the tests was aborted at CFBOT [1] with:
>> [09:47:00.735] dumping /tmp/cores/postgres-11-28182.core for
>> /tmp/cirrus-ci-build/build/tmp_install//usr/local/pgsql/bin/postgres
>> [09:47:01.035] [New LWP 28182]
>
> There was a bug in JIT part, here is the fix. Thanks for taking care of
> this!
Fixed a GCC warning in cirrus-ci, hope everything is fine now.
--
Best Regards
Andy Fan
Attachments:
[text/x-diff] v4-0001-shared-detoast-feature.patch (75.0K, ../../[email protected]/2-v4-0001-shared-detoast-feature.patch)
download | inline diff:
From 6dc858fae29486bea9125e7a3fb43a7081e62097 Mon Sep 17 00:00:00 2001
From: "yizhi.fzh" <[email protected]>
Date: Wed, 27 Dec 2023 18:43:56 +0800
Subject: [PATCH v4 1/1] shared detoast feature.
---
src/backend/executor/execExpr.c | 65 ++-
src/backend/executor/execExprInterp.c | 181 +++++++
src/backend/executor/execTuples.c | 130 +++++
src/backend/executor/execUtils.c | 5 +
src/backend/executor/nodeHashjoin.c | 2 +
src/backend/executor/nodeMergejoin.c | 2 +
src/backend/executor/nodeNestloop.c | 1 +
src/backend/jit/llvm/llvmjit_expr.c | 26 +-
src/backend/jit/llvm/llvmjit_types.c | 1 +
src/backend/nodes/bitmapset.c | 13 +
src/backend/optimizer/plan/createplan.c | 73 ++-
src/backend/optimizer/plan/setrefs.c | 529 +++++++++++++++----
src/include/executor/execExpr.h | 6 +
src/include/executor/tuptable.h | 60 +++
src/include/nodes/bitmapset.h | 1 +
src/include/nodes/execnodes.h | 5 +
src/include/nodes/plannodes.h | 50 ++
src/test/regress/sql/shared_detoast_slow.sql | 70 +++
18 files changed, 1114 insertions(+), 106 deletions(-)
create mode 100644 src/test/regress/sql/shared_detoast_slow.sql
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 91df2009be..4aeec8419f 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -932,22 +932,81 @@ ExecInitExprRec(Expr *node, ExprState *state,
}
else
{
+ int attnum;
+ Plan *plan = state->parent ? state->parent->plan : NULL;
+
/* regular user column */
scratch.d.var.attnum = variable->varattno - 1;
scratch.d.var.vartype = variable->vartype;
+ attnum = scratch.d.var.attnum;
+
switch (variable->varno)
{
case INNER_VAR:
- scratch.opcode = EEOP_INNER_VAR;
+
+ if (is_join_plan(plan) &&
+ bms_is_member(attnum,
+ ((JoinState *) state->parent)->inner_pre_detoast_attrs))
+ {
+ /* debug purpose. */
+ if (!jit_enabled)
+ {
+ elog(INFO,
+ "EEOP_INNER_VAR_TOAST: flags = %d costs=%.2f..%.2f, attnum: %d",
+ state->flags,
+ plan->startup_cost,
+ plan->total_cost,
+ attnum);
+ }
+ scratch.opcode = EEOP_INNER_VAR_TOAST;
+ }
+ else
+ {
+ scratch.opcode = EEOP_INNER_VAR;
+ }
break;
case OUTER_VAR:
- scratch.opcode = EEOP_OUTER_VAR;
+ if (is_join_plan(plan) &&
+ bms_is_member(attnum,
+ ((JoinState *) state->parent)->outer_pre_detoast_attrs))
+ {
+ /* debug purpose. */
+ if (!jit_enabled)
+ {
+ elog(INFO,
+ "EEOP_OUTER_VAR_TOAST: flags = %u costs=%.2f..%.2f, attnum: %d",
+ state->flags,
+ plan->startup_cost,
+ plan->total_cost,
+ attnum);
+ }
+ scratch.opcode = EEOP_OUTER_VAR_TOAST;
+ }
+ else
+ scratch.opcode = EEOP_OUTER_VAR;
break;
/* INDEX_VAR is handled by default case */
default:
- scratch.opcode = EEOP_SCAN_VAR;
+ if (is_scan_plan(plan) && bms_is_member(
+ attnum,
+ ((ScanState *) state->parent)->scan_pre_detoast_attrs))
+ {
+ if (!jit_enabled)
+ {
+ elog(INFO,
+ "EEOP_SCAN_VAR_TOAST: flags = %u costs=%.2f..%.2f, scanId: %d, attnum: %d",
+ state->flags,
+ plan->startup_cost,
+ plan->total_cost,
+ ((Scan *) plan)->scanrelid,
+ attnum);
+ }
+ scratch.opcode = EEOP_SCAN_VAR_TOAST;
+ }
+ else
+ scratch.opcode = EEOP_SCAN_VAR;
break;
}
}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 3c17cc6b1e..bd769fdeb6 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -57,6 +57,7 @@
#include "postgres.h"
#include "access/heaptoast.h"
+#include "access/detoast.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
#include "executor/execExpr.h"
@@ -157,6 +158,9 @@ static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
@@ -165,6 +169,9 @@ static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull
static Datum ExecJustInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
@@ -180,6 +187,43 @@ static pg_attribute_always_inline void ExecAggPlainTransByRef(AggState *aggstate
AggStatePerGroup pergroup,
ExprContext *aggcontext,
int setno);
+static inline void
+ExecSlotDetoastDatum(TupleTableSlot *slot, int attnum)
+{
+ if (!slot->tts_isnull[attnum] &&
+ VARATT_IS_EXTENDED(slot->tts_values[attnum]))
+ {
+ Datum oldDatum;
+ MemoryContext old = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ oldDatum = slot->tts_values[attnum];
+ slot->tts_values[attnum] = PointerGetDatum(detoast_attr(
+ (struct varlena *) oldDatum));
+ Assert(slot->tts_nvalid > attnum);
+ if (oldDatum != slot->tts_values[attnum])
+ slot->pre_detoasted_attrs = bms_add_member(slot->pre_detoasted_attrs, attnum);
+ MemoryContextSwitchTo(old);
+ }
+}
+
+/* JIT requires a non-static (and external?) function */
+void
+ExecSlotDetoastDatumExternal(TupleTableSlot *slot, int attnum)
+{
+ return ExecSlotDetoastDatum(slot, attnum);
+}
+
+
+static inline void
+ExecEvalToastVar(TupleTableSlot *slot,
+ ExprEvalStep *op,
+ int attnum)
+{
+ ExecSlotDetoastDatum(slot, attnum);
+
+ *op->resvalue = slot->tts_values[attnum];
+ *op->resnull = slot->tts_isnull[attnum];
+}
/*
* ScalarArrayOpExprHashEntry
@@ -295,6 +339,24 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustScanVar;
return;
}
+ if (step0 == EEOP_INNER_FETCHSOME &&
+ step1 == EEOP_INNER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustInnerVarToast;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_FETCHSOME &&
+ step1 == EEOP_OUTER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustOuterVarToast;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_FETCHSOME &&
+ step1 == EEOP_SCAN_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustScanVarToast;
+ return;
+ }
else if (step0 == EEOP_INNER_FETCHSOME &&
step1 == EEOP_ASSIGN_INNER_VAR)
{
@@ -330,6 +392,7 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustConst;
return;
}
+ /* ???? */
else if (step0 == EEOP_INNER_VAR)
{
state->evalfunc_private = (void *) ExecJustInnerVarVirt;
@@ -345,6 +408,21 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustScanVarVirt;
return;
}
+ else if (step0 == EEOP_INNER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustInnerVarVirtToast;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustOuterVarVirtToast;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustScanVarVirtToast;
+ return;
+ }
else if (step0 == EEOP_ASSIGN_INNER_VAR)
{
state->evalfunc_private = (void *) ExecJustAssignInnerVarVirt;
@@ -412,6 +490,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_INNER_VAR,
&&CASE_EEOP_OUTER_VAR,
&&CASE_EEOP_SCAN_VAR,
+ &&CASE_EEOP_INNER_VAR_TOAST,
+ &&CASE_EEOP_OUTER_VAR_TOAST,
+ &&CASE_EEOP_SCAN_VAR_TOAST,
&&CASE_EEOP_INNER_SYSVAR,
&&CASE_EEOP_OUTER_SYSVAR,
&&CASE_EEOP_SCAN_SYSVAR,
@@ -595,6 +676,25 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
*op->resvalue = scanslot->tts_values[attnum];
*op->resnull = scanslot->tts_isnull[attnum];
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_INNER_VAR_TOAST)
+ {
+ ExecEvalToastVar(innerslot, op, op->d.var.attnum);
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_OUTER_VAR_TOAST)
+ {
+ ExecEvalToastVar(outerslot, op, op->d.var.attnum);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SCAN_VAR_TOAST)
+ {
+ ExecEvalToastVar(scanslot, op, op->d.var.attnum);
EEO_NEXT();
}
@@ -2126,6 +2226,42 @@ ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
return ExecJustVarImpl(state, econtext->ecxt_scantuple, isnull);
}
+static pg_attribute_always_inline Datum
+ExecJustVarImplToast(ExprState *state, TupleTableSlot *slot, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum;
+
+ CheckOpSlotCompatibility(&state->steps[0], slot);
+
+ slot_getattr(slot, attnum + 1, isnull);
+
+ ExecSlotDetoastDatum(slot, attnum);
+
+ return slot->tts_values[attnum];
+}
+
+/* Simple reference to inner Var */
+static Datum
+ExecJustInnerVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_innertuple, isnull);
+}
+
+/* Simple reference to outer Var */
+static Datum
+ExecJustOuterVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_outertuple, isnull);
+}
+
+/* Simple reference to scan Var */
+static Datum
+ExecJustScanVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_scantuple, isnull);
+}
+
/* implementation of ExecJustAssign(Inner|Outer|Scan)Var */
static pg_attribute_always_inline Datum
ExecJustAssignVarImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
@@ -2264,6 +2400,51 @@ ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
return ExecJustVarVirtImpl(state, econtext->ecxt_scantuple, isnull);
}
+/* implementation of ExecJust(Inner|Outer|Scan)VarVirt */
+static pg_attribute_always_inline Datum
+ExecJustVarVirtImplToast(ExprState *state, TupleTableSlot *slot, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[0];
+ int attnum = op->d.var.attnum;
+
+ /*
+ * As it is guaranteed that a virtual slot is used, there never is a need
+ * to perform tuple deforming (nor would it be possible). Therefore
+ * execExpr.c has not emitted an EEOP_*_FETCHSOME step. Verify, as much as
+ * possible, that that determination was accurate.
+ */
+ Assert(TTS_IS_VIRTUAL(slot));
+ Assert(TTS_FIXED(slot));
+ Assert(attnum >= 0 && attnum < slot->tts_nvalid);
+
+ *isnull = slot->tts_isnull[attnum];
+
+ ExecSlotDetoastDatum(slot, attnum);
+
+ return slot->tts_values[attnum];
+}
+
+/* Like ExecJustInnerVar, optimized for virtual slots */
+static Datum
+ExecJustInnerVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_innertuple, isnull);
+}
+
+/* Like ExecJustOuterVar, optimized for virtual slots */
+static Datum
+ExecJustOuterVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_outertuple, isnull);
+}
+
+/* Like ExecJustScanVar, optimized for virtual slots */
+static Datum
+ExecJustScanVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_scantuple, isnull);
+}
+
/* implementation of ExecJustAssign(Inner|Outer|Scan)VarVirt */
static pg_attribute_always_inline Datum
ExecJustAssignVarVirtImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index a7aa2ee02b..08c96a42b1 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -79,6 +79,9 @@ static inline void tts_buffer_heap_store_tuple(TupleTableSlot *slot,
bool transfer_pin);
static void tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree);
+static Bitmapset *cal_final_pre_detoast_attrs(Bitmapset *plan_pre_detoast_attrs,
+ TupleDesc tupleDesc,
+ List *not_pre_detoast_vars);
const TupleTableSlotOps TTSOpsVirtual;
const TupleTableSlotOps TTSOpsHeapTuple;
@@ -176,6 +179,10 @@ tts_virtual_materialize(TupleTableSlot *slot)
if (att->attbyval || slot->tts_isnull[natt])
continue;
+ if (bms_is_member(natt, slot->pre_detoasted_attrs))
+ /* it has been in slot->tts_mcxt already. */
+ continue;
+
val = slot->tts_values[natt];
if (att->attlen == -1 &&
@@ -392,6 +399,13 @@ tts_heap_materialize(TupleTableSlot *slot)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_values is treated as non valid (tts_nvalid = 0), so let free the
+ * pre-detoast datum.
+ */
+ ExecFreePreDetoastDatum(slot);
+
}
static void
@@ -457,6 +471,9 @@ tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /* slot_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
@@ -567,6 +584,12 @@ tts_minimal_materialize(TupleTableSlot *slot)
mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_values is treated as non valid (tts_nvalid = 0), free the
+ * pre-detoast datum.
+ */
+ ExecFreePreDetoastDatum(slot);
}
static void
@@ -637,6 +660,9 @@ tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /* tts_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
@@ -771,6 +797,12 @@ tts_buffer_heap_materialize(TupleTableSlot *slot)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_nvalid = 0 means tts_values will be not reliable, so clear the
+ * information about pre-detoast-datum.
+ */
+ ExecFreePreDetoastDatum(slot);
}
static void
@@ -860,6 +892,7 @@ tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
{
BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot;
+
if (TTS_SHOULDFREE(slot))
{
/* materialized slot shouldn't have a buffer to release */
@@ -904,6 +937,8 @@ tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
*/
ReleaseBuffer(buffer);
}
+
+ ExecFreePreDetoastDatum(slot);
}
/*
@@ -1138,6 +1173,7 @@ MakeTupleTableSlot(TupleDesc tupleDesc,
slot->tts_tupleDescriptor = tupleDesc;
slot->tts_mcxt = CurrentMemoryContext;
slot->tts_nvalid = 0;
+ slot->pre_detoasted_attrs = NULL;
if (tupleDesc != NULL)
{
@@ -1810,12 +1846,30 @@ void
ExecInitScanTupleSlot(EState *estate, ScanState *scanstate,
TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
{
+ Scan *splan = (Scan *) scanstate->ps.plan;
+
scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
tupledesc, tts_ops);
scanstate->ps.scandesc = tupledesc;
scanstate->ps.scanopsfixed = tupledesc != NULL;
scanstate->ps.scanops = tts_ops;
scanstate->ps.scanopsset = true;
+
+ if (is_scan_plan((Plan *) splan))
+ {
+ /*
+ * We may run detoast in Qual or Projection, but all of them happen at
+ * the ss_ScanTupleSlot rather than ps_ResultTupleSlot. So we can only
+ * take care of the ss_ScanTupleSlot.
+ *
+ * the ps_ResultTupleSlot may also have detoast on its parent node,
+ * like as a inner or outer slot in join case, the pre_detoast on
+ * these slot.
+ */
+ scanstate->scan_pre_detoast_attrs = cal_final_pre_detoast_attrs(splan->reference_attrs,
+ tupledesc,
+ splan->plan.forbid_pre_detoast_vars);
+ }
}
/* ----------------
@@ -2336,3 +2390,79 @@ end_tup_output(TupOutputState *tstate)
ExecDropSingleTupleTableSlot(tstate->slot);
pfree(tstate);
}
+
+static Bitmapset *
+cal_final_pre_detoast_attrs(Bitmapset *plan_pre_detoast_attrs,
+ TupleDesc tupleDesc,
+ List *not_pre_detoast_vars)
+{
+ Bitmapset *final = NULL,
+ *toast_attrs = NULL,
+ *forbid_pre_detoast_attrs = NULL;
+
+ int i;
+ ListCell *lc;
+
+ if (bms_is_empty(plan_pre_detoast_attrs))
+ return NULL;
+
+ /*
+ * there is no exact data type in create_plan or set_plan_refs stage, so
+ * plan_pre_detoast_attrs may have some attribute which is not toast attrs
+ * at all, which should be removed.
+ */
+ for (i = 0; i < tupleDesc->natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(tupleDesc, i);
+
+ if (attr->attlen == -1 && attr->attstorage != TYPSTORAGE_PLAIN)
+ toast_attrs = bms_add_member(toast_attrs, attr->attnum - 1);
+ }
+
+ final = bms_intersect(plan_pre_detoast_attrs, toast_attrs);
+
+ /*
+ * Due to the fact of detoast-datum will make the tuple bigger which is
+ * bad for some nodes like Sort/Hash, to avoid performance regression,
+ * such attribute should be removed as well.
+ */
+ foreach(lc, not_pre_detoast_vars)
+ {
+ Var *var = lfirst_node(Var, lc);
+
+ forbid_pre_detoast_attrs = bms_add_member(forbid_pre_detoast_attrs, var->varattno - 1);
+ }
+
+ final = bms_del_members(final, forbid_pre_detoast_attrs);
+
+ bms_free(toast_attrs);
+ bms_free(forbid_pre_detoast_attrs);
+
+ return final;
+}
+
+
+/* Input slot, result slot. scan slot? */
+void
+SetPredetoastAttrsForScan(ScanState *scanstate)
+{
+}
+
+void
+SetPredetoastAttrsForJoin(JoinState *j)
+{
+ PlanState *outerstate = outerPlanState(j);
+ PlanState *innerstate = innerPlanState(j);
+
+ /* Input slot, result slot. scan slot? */
+
+ j->outer_pre_detoast_attrs = cal_final_pre_detoast_attrs(
+ ((Join *) j->ps.plan)->outer_reference_attrs,
+ outerstate->ps_ResultTupleDesc,
+ outerstate->plan->forbid_pre_detoast_vars);
+
+ j->inner_pre_detoast_attrs = cal_final_pre_detoast_attrs(
+ ((Join *) j->ps.plan)->inner_reference_attrs,
+ innerstate->ps_ResultTupleDesc,
+ innerstate->plan->forbid_pre_detoast_vars);
+}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index cff5dc723e..43de95f5b0 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -572,6 +572,11 @@ ExecConditionalAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc,
planstate->resultopsset = planstate->scanopsset;
planstate->resultopsfixed = planstate->scanopsfixed;
planstate->resultops = planstate->scanops;
+
+ /*
+ * XXX: can I make sure the ps_ResultTupleDesc is set all the time?
+ */
+ Assert(planstate->ps_ResultTupleDesc != NULL);
}
else
{
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 1cbec4647c..19a05ed624 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -756,6 +756,8 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate, eflags);
innerDesc = ExecGetResultType(innerPlanState(hjstate));
+ SetPredetoastAttrsForJoin((JoinState *) hjstate);
+
/*
* Initialize result slot, type and projection.
*/
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index c1a8ca2464..be7cbd7f30 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -1497,6 +1497,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
(eflags | EXEC_FLAG_MARK));
innerDesc = ExecGetResultType(innerPlanState(mergestate));
+ SetPredetoastAttrsForJoin((JoinState *) mergestate);
+
/*
* For certain types of inner child nodes, it is advantageous to issue
* MARK every time we advance past an inner tuple we will never return to.
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 06fa0a9b31..2d40d19192 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -306,6 +306,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
*/
ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
+ SetPredetoastAttrsForJoin((JoinState *) nlstate);
/*
* initialize child expressions
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 33161d812f..56c10c9e4d 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -396,30 +396,52 @@ llvm_compile_expr(ExprState *state)
case EEOP_INNER_VAR:
case EEOP_OUTER_VAR:
case EEOP_SCAN_VAR:
+ case EEOP_INNER_VAR_TOAST:
+ case EEOP_OUTER_VAR_TOAST:
+ case EEOP_SCAN_VAR_TOAST:
{
LLVMValueRef value,
isnull;
LLVMValueRef v_attnum;
LLVMValueRef v_values;
LLVMValueRef v_nulls;
+ LLVMValueRef v_slot;
- if (opcode == EEOP_INNER_VAR)
+ if (opcode == EEOP_INNER_VAR || opcode == EEOP_INNER_VAR_TOAST)
{
+ v_slot = v_innerslot;
v_values = v_innervalues;
v_nulls = v_innernulls;
}
- else if (opcode == EEOP_OUTER_VAR)
+ else if (opcode == EEOP_OUTER_VAR || opcode == EEOP_OUTER_VAR_TOAST)
{
+ v_slot = v_outerslot;
v_values = v_outervalues;
v_nulls = v_outernulls;
}
else
{
+ v_slot = v_scanslot;
v_values = v_scanvalues;
v_nulls = v_scannulls;
}
v_attnum = l_int32_const(lc, op->d.var.attnum);
+
+ if (opcode == EEOP_INNER_VAR_TOAST ||
+ opcode == EEOP_OUTER_VAR_TOAST ||
+ opcode == EEOP_SCAN_VAR_TOAST)
+ {
+ LLVMValueRef params[2];
+
+ params[0] = v_slot;
+ params[1] = l_int32_const(lc, op->d.var.attnum);
+ l_call(b,
+ llvm_pg_var_func_type("ExecSlotDetoastDatumExternal"),
+ llvm_pg_func(mod, "ExecSlotDetoastDatumExternal"),
+ params, lengthof(params), "");
+ }
+
value = l_load_gep1(b, TypeSizeT, v_values, v_attnum, "");
isnull = l_load_gep1(b, TypeStorageBool, v_nulls, v_attnum, "");
LLVMBuildStore(b, value, v_resvaluep);
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 5212f529c8..11a11028b8 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -177,4 +177,5 @@ void *referenced_functions[] =
strlen,
varsize_any,
ExecInterpExprStillValid,
+ ExecSlotDetoastDatumExternal,
};
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index f4b61085be..e0eb150e5b 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -774,6 +774,19 @@ bms_membership(const Bitmapset *a)
* foo = bms_add_member(foo, x);
*/
+/*
+ * does this break commit 00b41463c21615f9bf3927f207e37f9e215d32e6?
+ * but I just found alloc memory and free the memory is too bad
+ * for this current feature. So let see ...;
+ */
+void
+bms_zero(Bitmapset *a)
+{
+ if (a == NULL)
+ return;
+
+ memset(a->words, 0, a->nwords * sizeof(bitmapword));
+}
/*
* bms_add_member - add a specified member to set
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ca619eab94..3f3aea73ce 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -42,6 +42,7 @@
#include "partitioning/partprune.h"
#include "utils/lsyscache.h"
+extern bool jit_enabled;
/*
* Flag bits that can appear in the flags argument of create_plan_recurse().
@@ -314,7 +315,8 @@ static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan,
List *mergeActionLists, int epqParam);
static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
GatherMergePath *best_path);
-
+static void set_plan_not_detoast_attrs_recurse(Plan *plan,
+ List *recheck_list);
/*
* create_plan
@@ -346,6 +348,8 @@ create_plan(PlannerInfo *root, Path *best_path)
/* Recursively process the path tree, demanding the correct tlist result */
plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST);
+ set_plan_not_detoast_attrs_recurse(plan, NIL);
+
/*
* Make sure the topmost plan node's targetlist exposes the original
* column names and other decorative info. Targetlists generated within
@@ -378,6 +382,68 @@ create_plan(PlannerInfo *root, Path *best_path)
return plan;
}
+/*
+ * set_plan_not_pre_detoast_vars
+ *
+ * set the toast_attrs according recheck_list.
+ *
+ * recheck_list = NIL means we need to do thing.
+ */
+static void
+set_plan_not_pre_detoast_vars(Plan *plan, List *recheck_list)
+{
+ ListCell *lc;
+ Var *var;
+
+ if (recheck_list == NIL)
+ return;
+
+ foreach(lc, plan->targetlist)
+ {
+ TargetEntry *te = lfirst_node(TargetEntry, lc);
+
+ if (!IsA(te->expr, Var))
+ continue;
+ var = castNode(Var, te->expr);
+ if (var->varattno <= 0)
+ continue;
+ if (list_member(recheck_list, var))
+ /* pass the recheck */
+ plan->forbid_pre_detoast_vars = lappend(plan->forbid_pre_detoast_vars, var);
+ }
+}
+
+
+static void
+set_plan_not_detoast_attrs_recurse(Plan *plan, List *recheck_list)
+{
+ if (plan == NULL)
+ return;
+
+ set_plan_not_pre_detoast_vars(plan, recheck_list);
+
+ if (IsA(plan, Sort) || IsA(plan, Memoize) || IsA(plan, WindowAgg) ||
+ IsA(plan, Hash) || IsA(plan, Material) || IsA(plan, IncrementalSort))
+ {
+ List *subplan_exprs = get_tlist_exprs(plan->lefttree->targetlist, true);
+
+ set_plan_not_pre_detoast_vars(plan, subplan_exprs);
+ set_plan_not_detoast_attrs_recurse(plan->lefttree, subplan_exprs);
+ }
+ else if (IsA(plan, HashJoin) && castNode(HashJoin, plan)->left_small_tlist)
+ {
+ List *subplan_exprs = get_tlist_exprs(plan->lefttree->targetlist, true);
+
+ set_plan_not_detoast_attrs_recurse(plan->lefttree, subplan_exprs);
+ set_plan_not_detoast_attrs_recurse(plan->righttree, plan->forbid_pre_detoast_vars);
+ }
+ else
+ {
+ set_plan_not_detoast_attrs_recurse(plan->lefttree, plan->forbid_pre_detoast_vars);
+ set_plan_not_detoast_attrs_recurse(plan->righttree, plan->forbid_pre_detoast_vars);
+ }
+}
+
/*
* create_plan_recurse
* Recursive guts of create_plan().
@@ -4884,6 +4950,11 @@ create_hashjoin_plan(PlannerInfo *root,
copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
+ join_plan->left_small_tlist = (best_path->num_batches > 1);
+
+ if (!jit_enabled)
+ elog(INFO, "num_batches = %d", best_path->num_batches);
+
return join_plan;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 22a1fa29f3..b81ddf72c3 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -27,6 +27,7 @@
#include "optimizer/tlist.h"
#include "parser/parse_relation.h"
#include "tcop/utility.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -55,11 +56,27 @@ typedef struct
tlist_vinfo vars[FLEXIBLE_ARRAY_MEMBER]; /* has num_vars entries */
} indexed_tlist;
+typedef struct
+{
+ /* var is added into existing_attrs for the first time. */
+ Bitmapset *existing_attrs;
+ /* following add to the final_ref_attrs. */
+ Bitmapset **final_ref_attrs;
+} intermediate_var_ref_context;
+
+typedef struct
+{
+ int level_added;
+ int level;
+} intermediate_level_context;
+
typedef struct
{
PlannerInfo *root;
int rtoffset;
double num_exec;
+ intermediate_level_context level_ctx;
+ intermediate_var_ref_context scan_reference_attrs;
} fix_scan_expr_context;
typedef struct
@@ -71,6 +88,9 @@ typedef struct
int rtoffset;
NullingRelsMatch nrm_match;
double num_exec;
+ intermediate_level_context level_ctx;
+ intermediate_var_ref_context outer_reference_attrs;
+ intermediate_var_ref_context inner_reference_attrs;
} fix_join_expr_context;
typedef struct
@@ -127,8 +147,8 @@ typedef struct
(((con)->consttype == REGCLASSOID || (con)->consttype == OIDOID) && \
!(con)->constisnull)
-#define fix_scan_list(root, lst, rtoffset, num_exec) \
- ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec))
+#define fix_scan_list(root, lst, rtoffset, num_exec, pre_detoast_attrs) \
+ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec, pre_detoast_attrs))
static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing);
static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
@@ -158,7 +178,8 @@ static Plan *set_mergeappend_references(PlannerInfo *root,
static void set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset);
static Relids offset_relid_set(Relids relids, int rtoffset);
static Node *fix_scan_expr(PlannerInfo *root, Node *node,
- int rtoffset, double num_exec);
+ int rtoffset, double num_exec,
+ Bitmapset **scan_reference_attrs);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
@@ -190,7 +211,10 @@ static List *fix_join_expr(PlannerInfo *root,
Index acceptable_rel,
int rtoffset,
NullingRelsMatch nrm_match,
- double num_exec);
+ double num_exec,
+ Bitmapset **outer_reference_attrs,
+ Bitmapset **inner_reference_attrs);
+
static Node *fix_join_expr_mutator(Node *node,
fix_join_expr_context *context);
static Node *fix_upper_expr(PlannerInfo *root,
@@ -628,10 +652,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_SampleScan:
@@ -641,13 +671,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs
+ );
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tablesample = (TableSampleClause *)
fix_scan_expr(root, (Node *) splan->tablesample,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_IndexScan:
@@ -657,28 +694,40 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
+
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
splan->indexqual =
fix_scan_list(root, splan->indexqual,
- rtoffset, 1);
+ rtoffset, 1, &splan->scan.reference_attrs);
splan->indexqualorig =
fix_scan_list(root, splan->indexqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->indexorderby =
fix_scan_list(root, splan->indexorderby,
- rtoffset, 1);
+ rtoffset, 1, &splan->scan.reference_attrs);
splan->indexorderbyorig =
fix_scan_list(root, splan->indexorderbyorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan), &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_IndexOnlyScan:
{
IndexOnlyScan *splan = (IndexOnlyScan *) plan;
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
+
return set_indexonlyscan_references(root, splan, rtoffset);
}
break;
@@ -691,10 +740,15 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->scan.plan.targetlist == NIL);
Assert(splan->scan.plan.qual == NIL);
splan->indexqual =
- fix_scan_list(root, splan->indexqual, rtoffset, 1);
+ fix_scan_list(root, splan->indexqual, rtoffset, 1,
+ &splan->scan.reference_attrs);
splan->indexqualorig =
fix_scan_list(root, splan->indexqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_BitmapHeapScan:
@@ -704,13 +758,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->bitmapqualorig =
fix_scan_list(root, splan->bitmapqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_TidScan:
@@ -720,13 +781,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tidquals =
fix_scan_list(root, splan->tidquals,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_TidRangeScan:
@@ -736,17 +804,25 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tidrangequals =
fix_scan_list(root, splan->tidrangequals,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_SubqueryScan:
/* Needs special treatment, see comments below */
+ /* XXX: shall I do anything? */
return set_subqueryscan_references(root,
(SubqueryScan *) plan,
rtoffset);
@@ -757,12 +833,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->functions =
- fix_scan_list(root, splan->functions, rtoffset, 1);
+ fix_scan_list(root, splan->functions, rtoffset, 1,
+ &splan->scan.reference_attrs);
+
}
break;
case T_TableFuncScan:
@@ -772,13 +852,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
splan->tablefunc = (TableFunc *)
fix_scan_expr(root, (Node *) splan->tablefunc,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
}
break;
case T_ValuesScan:
@@ -788,13 +872,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->values_lists =
fix_scan_list(root, splan->values_lists,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
}
break;
case T_CteScan:
@@ -804,10 +891,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_NamedTuplestoreScan:
@@ -817,10 +910,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
}
break;
case T_WorkTableScan:
@@ -830,10 +925,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
}
break;
case T_ForeignScan:
@@ -873,7 +970,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
mplan->param_exprs = fix_scan_list(root, mplan->param_exprs,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan),
+ NULL);
break;
}
@@ -933,9 +1031,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->plan.qual == NIL);
splan->limitOffset =
- fix_scan_expr(root, splan->limitOffset, rtoffset, 1);
+ fix_scan_expr(root, splan->limitOffset, rtoffset, 1, NULL);
splan->limitCount =
- fix_scan_expr(root, splan->limitCount, rtoffset, 1);
+ fix_scan_expr(root, splan->limitCount, rtoffset, 1, NULL);
}
break;
case T_Agg:
@@ -988,17 +1086,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
* variable refs, so fix_scan_expr works for them.
*/
wplan->startOffset =
- fix_scan_expr(root, wplan->startOffset, rtoffset, 1);
+ fix_scan_expr(root, wplan->startOffset, rtoffset, 1, NULL);
wplan->endOffset =
- fix_scan_expr(root, wplan->endOffset, rtoffset, 1);
+ fix_scan_expr(root, wplan->endOffset, rtoffset, 1, NULL);
wplan->runCondition = fix_scan_list(root,
wplan->runCondition,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan), NULL);
wplan->runConditionOrig = fix_scan_list(root,
wplan->runConditionOrig,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_Result:
@@ -1038,14 +1136,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->plan.targetlist =
fix_scan_list(root, splan->plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
splan->plan.qual =
fix_scan_list(root, splan->plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan), NULL);
}
/* resconstantqual can't contain any subplan variable refs */
splan->resconstantqual =
- fix_scan_expr(root, splan->resconstantqual, rtoffset, 1);
+ fix_scan_expr(root, splan->resconstantqual, rtoffset, 1, NULL);
}
break;
case T_ProjectSet:
@@ -1061,7 +1159,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->withCheckOptionLists =
fix_scan_list(root, splan->withCheckOptionLists,
- rtoffset, 1);
+ rtoffset, 1, NULL);
if (splan->returningLists)
{
@@ -1118,18 +1216,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
fix_join_expr(root, splan->onConflictSet,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan),
+ NULL, NULL);
splan->onConflictWhere = (Node *)
fix_join_expr(root, (List *) splan->onConflictWhere,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan),
+ NULL, NULL);
pfree(itlist);
splan->exclRelTlist =
- fix_scan_list(root, splan->exclRelTlist, rtoffset, 1);
+ fix_scan_list(root, splan->exclRelTlist, rtoffset, 1, NULL);
}
/*
@@ -1182,7 +1282,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
resultrel,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan),
+ NULL, NULL);
/* Fix quals too. */
action->qual = (Node *) fix_join_expr(root,
@@ -1191,7 +1292,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
resultrel,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL(plan));
+ NUM_EXEC_QUAL(plan),
+ NULL, NULL);
}
}
}
@@ -1356,13 +1458,16 @@ set_indexonlyscan_references(PlannerInfo *root,
NUM_EXEC_QUAL((Plan *) plan));
/* indexqual is already transformed to reference index columns */
plan->indexqual = fix_scan_list(root, plan->indexqual,
- rtoffset, 1);
+ rtoffset, 1,
+ &plan->scan.reference_attrs);
/* indexorderby is already transformed to reference index columns */
plan->indexorderby = fix_scan_list(root, plan->indexorderby,
- rtoffset, 1);
+ rtoffset, 1,
+ &plan->scan.reference_attrs);
/* indextlist must NOT be transformed to reference index columns */
plan->indextlist = fix_scan_list(root, plan->indextlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) plan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan),
+ &plan->scan.reference_attrs);
pfree(index_itlist);
@@ -1409,10 +1514,10 @@ set_subqueryscan_references(PlannerInfo *root,
plan->scan.scanrelid += rtoffset;
plan->scan.plan.targetlist =
fix_scan_list(root, plan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) plan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan), NULL);
plan->scan.plan.qual =
fix_scan_list(root, plan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) plan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) plan), NULL);
result = (Plan *) plan;
}
@@ -1612,7 +1717,7 @@ set_foreignscan_references(PlannerInfo *root,
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
fscan->fdw_scan_tlist =
fix_scan_list(root, fscan->fdw_scan_tlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan), NULL);
}
else
{
@@ -1622,16 +1727,16 @@ set_foreignscan_references(PlannerInfo *root,
*/
fscan->scan.plan.targetlist =
fix_scan_list(root, fscan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan), NULL);
fscan->scan.plan.qual =
fix_scan_list(root, fscan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
fscan->fdw_exprs =
fix_scan_list(root, fscan->fdw_exprs,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
fscan->fdw_recheck_quals =
fix_scan_list(root, fscan->fdw_recheck_quals,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
}
fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset);
@@ -1690,20 +1795,20 @@ set_customscan_references(PlannerInfo *root,
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
cscan->custom_scan_tlist =
fix_scan_list(root, cscan->custom_scan_tlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan), NULL);
}
else
{
/* Adjust tlist, qual, custom_exprs in the standard way */
cscan->scan.plan.targetlist =
fix_scan_list(root, cscan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan), NULL);
cscan->scan.plan.qual =
fix_scan_list(root, cscan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan), NULL);
cscan->custom_exprs =
fix_scan_list(root, cscan->custom_exprs,
- rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan), NULL);
}
/* Adjust child plan-nodes recursively, if needed */
@@ -2111,6 +2216,95 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
return (Node *) bestplan;
}
+
+static inline void
+setup_intermediate_level_ctx(intermediate_level_context * ctx)
+{
+ ctx->level = 0;
+ ctx->level_added = false;
+}
+
+static inline void
+setup_intermediate_var_ref_ctx(intermediate_var_ref_context * ctx, Bitmapset **final_ref_attrs)
+{
+ ctx->existing_attrs = NULL;
+ ctx->final_ref_attrs = final_ref_attrs;
+}
+
+/*
+ * increase_level_for_pre_detoast
+ * Check if the given Expr could detoast a Var directly, if yes,
+ * increase the level and return true. otherwise return false;
+ */
+static inline void
+increase_level_for_pre_detoast(Node *node, intermediate_level_context * ctx)
+{
+ /* The following nodes is impossible to detoast a Var directly. */
+ if (IsA(node, List) || IsA(node, TargetEntry) || IsA(node, NullTest))
+ {
+ ctx->level_added = false;
+ }
+ else if (IsA(node, FuncExpr) && castNode(FuncExpr, node)->funcid == F_PG_COLUMN_COMPRESSION)
+ {
+ /* let's not detoast first so that pg_column_compression works. */
+ ctx->level_added = false;
+ }
+ else
+ {
+ ctx->level_added = true;
+ ctx->level += 1;
+ }
+}
+
+static inline void
+decreased_level_for_pre_detoast(intermediate_level_context * ctx)
+{
+ if (ctx->level_added)
+ ctx->level -= 1;
+
+ ctx->level_added = false;
+}
+
+/*
+ * add_pre_detoast_vars
+ * add the var's information into pre_detoast_attrs when the check is pass.
+ */
+static inline void
+add_pre_detoast_vars(intermediate_level_context * level_ctx,
+ intermediate_var_ref_context * ctx,
+ Var *var)
+{
+ int attno;
+
+ if (level_ctx->level <= 1 || ctx->final_ref_attrs == NULL || var->varattno <= 0)
+ return;
+
+ attno = var->varattno - 1;
+ if (bms_is_member(attno, ctx->existing_attrs))
+ {
+ /* not the first time to access it, add it to final result. */
+ *ctx->final_ref_attrs = bms_add_member(*ctx->final_ref_attrs, attno);
+ }
+ else
+ {
+ /* first time. */
+ ctx->existing_attrs = bms_add_member(ctx->existing_attrs, attno);
+
+ /*
+ * XXX:
+ *
+ * The above strategy doesn't help to detect if a Var is detoast
+ * twice. Reasons are: 1. the context is not maintain in Plan node
+ * level. so if it is detoast at targetlist and qual, we can't detect
+ * it. 2. even we can make it at plan node, it still doesn't help for
+ * the among-nodes case.
+ *
+ * So for now, I just disable it.
+ */
+ *ctx->final_ref_attrs = bms_add_member(*ctx->final_ref_attrs, attno);
+ }
+}
+
/*
* fix_scan_expr
* Do set_plan_references processing on a scan-level expression
@@ -2130,13 +2324,16 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
* if that seems safe.
*/
static Node *
-fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
+fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset,
+ double num_exec, Bitmapset **scan_reference_attrs)
{
fix_scan_expr_context context;
context.root = root;
context.rtoffset = rtoffset;
context.num_exec = num_exec;
+ setup_intermediate_level_ctx(&context.level_ctx);
+ setup_intermediate_var_ref_ctx(&context.scan_reference_attrs, scan_reference_attrs);
if (rtoffset != 0 ||
root->multiexpr_params != NIL ||
@@ -2167,8 +2364,13 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
static Node *
fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
{
+ Node *n;
+
if (node == NULL)
return NULL;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
if (IsA(node, Var))
{
Var *var = copyVar((Var *) node);
@@ -2186,10 +2388,16 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
var->varno += context->rtoffset;
if (var->varnosyn > 0)
var->varnosyn += context->rtoffset;
+
+ add_pre_detoast_vars(&context->level_ctx, &context->scan_reference_attrs, var);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) var;
}
if (IsA(node, Param))
+ {
+ decreased_level_for_pre_detoast(&context->level_ctx);
return fix_param_node(context->root, (Param *) node);
+ }
if (IsA(node, Aggref))
{
Aggref *aggref = (Aggref *) node;
@@ -2199,8 +2407,10 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
aggparam = find_minmax_agg_replacement_param(context->root, aggref);
if (aggparam != NULL)
{
+ decreased_level_for_pre_detoast(&context->level_ctx);
/* Make a copy of the Param for paranoia's sake */
return (Node *) copyObject(aggparam);
+
}
/* If no match, just fall through to process it normally */
}
@@ -2210,6 +2420,7 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
Assert(!IS_SPECIAL_VARNO(cexpr->cvarno));
cexpr->cvarno += context->rtoffset;
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) cexpr;
}
if (IsA(node, PlaceHolderVar))
@@ -2218,29 +2429,52 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
PlaceHolderVar *phv = (PlaceHolderVar *) node;
/* XXX can we assert something about phnullingrels? */
- return fix_scan_expr_mutator((Node *) phv->phexpr, context);
+ Node *n2 = fix_scan_expr_mutator((Node *) phv->phexpr, context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n2;
}
if (IsA(node, AlternativeSubPlan))
- return fix_scan_expr_mutator(fix_alternative_subplan(context->root,
- (AlternativeSubPlan *) node,
- context->num_exec),
- context);
+ {
+ Node *n2 = fix_scan_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n2;
+ }
fix_expr_common(context->root, node);
- return expression_tree_mutator(node, fix_scan_expr_mutator,
- (void *) context);
+ n = expression_tree_mutator(node, fix_scan_expr_mutator, (void *) context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
}
static bool
fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
{
+ bool ret;
+
if (node == NULL)
return false;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
+ if (IsA(node, Var))
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->scan_reference_attrs,
+ castNode(Var, node));
+ }
Assert(!(IsA(node, Var) && ((Var *) node)->varno == ROWID_VAR));
Assert(!IsA(node, PlaceHolderVar));
Assert(!IsA(node, AlternativeSubPlan));
fix_expr_common(context->root, node);
- return expression_tree_walker(node, fix_scan_expr_walker,
- (void *) context);
+ ret = expression_tree_walker(node, fix_scan_expr_walker,
+ (void *) context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret;
}
/*
@@ -2276,7 +2510,10 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs
+ );
/* Now do join-type-specific stuff */
if (IsA(join, NestLoop))
@@ -2323,7 +2560,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
}
else if (IsA(join, HashJoin))
{
@@ -2336,7 +2575,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
/*
* HashJoin's hashkeys are used to look for matching tuples from its
@@ -2368,7 +2609,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
(join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
- NUM_EXEC_TLIST((Plan *) join));
+ NUM_EXEC_TLIST((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
join->plan.qual = fix_join_expr(root,
join->plan.qual,
outer_itlist,
@@ -2376,8 +2619,20 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
(join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
- NUM_EXEC_QUAL((Plan *) join));
-
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
+
+ join->plan.forbid_pre_detoast_vars = fix_join_expr(root,
+ join->plan.forbid_pre_detoast_vars,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset,
+ (join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
+ NUM_EXEC_TLIST((Plan *) join),
+ NULL,
+ NULL);
pfree(outer_itlist);
pfree(inner_itlist);
}
@@ -3010,9 +3265,12 @@ fix_join_expr(PlannerInfo *root,
Index acceptable_rel,
int rtoffset,
NullingRelsMatch nrm_match,
- double num_exec)
+ double num_exec,
+ Bitmapset **outer_reference_attrs,
+ Bitmapset **inner_reference_attrs)
{
fix_join_expr_context context;
+ List *ret;
context.root = root;
context.outer_itlist = outer_itlist;
@@ -3021,16 +3279,30 @@ fix_join_expr(PlannerInfo *root,
context.rtoffset = rtoffset;
context.nrm_match = nrm_match;
context.num_exec = num_exec;
- return (List *) fix_join_expr_mutator((Node *) clauses, &context);
+
+ setup_intermediate_level_ctx(&context.level_ctx);
+ setup_intermediate_var_ref_ctx(&context.outer_reference_attrs, outer_reference_attrs);
+ setup_intermediate_var_ref_ctx(&context.inner_reference_attrs, inner_reference_attrs);
+
+ ret = (List *) fix_join_expr_mutator((Node *) clauses, &context);
+
+ bms_free(context.outer_reference_attrs.existing_attrs);
+ bms_free(context.inner_reference_attrs.existing_attrs);
+
+ return ret;
}
static Node *
fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
{
Var *newvar;
+ Node *ret_node;
if (node == NULL)
return NULL;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
if (IsA(node, Var))
{
Var *var = (Var *) node;
@@ -3044,7 +3316,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->rtoffset,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* then in the inner. */
@@ -3056,7 +3334,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->rtoffset,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* If it's for acceptable_rel, adjust and return it */
@@ -3066,6 +3350,9 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
var->varno += context->rtoffset;
if (var->varnosyn > 0)
var->varnosyn += context->rtoffset;
+ /* XXX acceptable_rel? we can ignore it for safety. */
+ decreased_level_for_pre_detoast(&context->level_ctx);
+
return (Node *) var;
}
@@ -3084,22 +3371,38 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
OUTER_VAR,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
if (context->inner_itlist && context->inner_itlist->has_ph_vars)
{
+
newvar = search_indexed_tlist_for_phv(phv,
context->inner_itlist,
INNER_VAR,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* If not supplied by input plans, evaluate the contained expr */
/* XXX can we assert something about phnullingrels? */
- return fix_join_expr_mutator((Node *) phv->phexpr, context);
+ ret_node = fix_join_expr_mutator((Node *) phv->phexpr, context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
}
+
/* Try matching more complex expressions too, if tlists have any */
if (context->outer_itlist && context->outer_itlist->has_non_vars)
{
@@ -3107,7 +3410,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->outer_itlist,
OUTER_VAR);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
if (context->inner_itlist && context->inner_itlist->has_non_vars)
{
@@ -3115,20 +3424,36 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->inner_itlist,
INNER_VAR);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* Special cases (apply only AFTER failing to match to lower tlist) */
if (IsA(node, Param))
- return fix_param_node(context->root, (Param *) node);
+ {
+ ret_node = fix_param_node(context->root, (Param *) node);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
+ }
if (IsA(node, AlternativeSubPlan))
- return fix_join_expr_mutator(fix_alternative_subplan(context->root,
- (AlternativeSubPlan *) node,
- context->num_exec),
- context);
+ {
+ ret_node = fix_join_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
+ }
fix_expr_common(context->root, node);
- return expression_tree_mutator(node,
- fix_join_expr_mutator,
- (void *) context);
+ ret_node = expression_tree_mutator(node,
+ fix_join_expr_mutator,
+ (void *) context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
}
/*
@@ -3163,7 +3488,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
* varno = newvarno, varattno = resno of corresponding targetlist element.
* The original tree is not modified.
*/
-static Node *
+static Node * /* XXX: shall I care about this for shared
+ * detoast optimization? */
fix_upper_expr(PlannerInfo *root,
Node *node,
indexed_tlist *subplan_itlist,
@@ -3318,7 +3644,10 @@ set_returning_clause_references(PlannerInfo *root,
resultRelation,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_TLIST(topplan));
+ NUM_EXEC_TLIST(topplan),
+ NULL,
+ NULL
+ );
pfree(itlist);
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index a20c539a25..9a31e083dd 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -77,6 +77,11 @@ typedef enum ExprEvalOp
EEOP_OUTER_VAR,
EEOP_SCAN_VAR,
+ /* compute non-system Var value with shared-detoast-datum logic */
+ EEOP_INNER_VAR_TOAST,
+ EEOP_OUTER_VAR_TOAST,
+ EEOP_SCAN_VAR_TOAST,
+
/* compute system Var value */
EEOP_INNER_SYSVAR,
EEOP_OUTER_SYSVAR,
@@ -826,5 +831,6 @@ extern void ExecEvalAggOrderedTransDatum(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecSlotDetoastDatumExternal(TupleTableSlot *slot, int attnum);
#endif /* EXEC_EXPR_H */
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 6133dbcd0a..5b42eb65f8 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,6 +18,7 @@
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/tupdesc.h"
+#include "nodes/bitmapset.h"
#include "storage/buf.h"
/*----------
@@ -128,6 +129,11 @@ typedef struct TupleTableSlot
MemoryContext tts_mcxt; /* slot itself is in this context */
ItemPointerData tts_tid; /* stored tuple's tid */
Oid tts_tableOid; /* table oid of tuple */
+
+ /*
+ * The attributes populated by EEOP_{INNER/OUTER/SCAN}_VAR_TOAST step.
+ */
+ Bitmapset *pre_detoasted_attrs;
} TupleTableSlot;
/* routines for a TupleTableSlot implementation */
@@ -426,12 +432,38 @@ slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
return slot->tts_ops->getsysattr(slot, attnum, isnull);
}
+static inline void
+ExecFreePreDetoastDatum(TupleTableSlot *slot)
+{
+ int attnum;
+
+ if (bms_is_empty(slot->pre_detoasted_attrs))
+ return;
+
+ attnum = -1;
+ /* free the memory used by pre-detoasted datum and reset the flags. */
+ while ((attnum = bms_next_member(slot->pre_detoasted_attrs, attnum)) >= 0)
+ {
+ pfree((void *) slot->tts_values[attnum]);
+ }
+
+ /*
+ * bms_free each time cost too much, so just zero these bits and keep its
+ * memory, just like what we did for TupleTableSlot. but.. see the
+ * comments about the bms_zero.
+ */
+ bms_zero(slot->pre_detoasted_attrs);
+}
+
+
/*
* ExecClearTuple - clear the slot's contents
*/
static inline TupleTableSlot *
ExecClearTuple(TupleTableSlot *slot)
{
+ ExecFreePreDetoastDatum(slot);
+
slot->tts_ops->clear(slot);
return slot;
@@ -450,6 +482,10 @@ ExecClearTuple(TupleTableSlot *slot)
static inline void
ExecMaterializeSlot(TupleTableSlot *slot)
{
+ /*
+ * XXX: pre_detoasted_attrs doesn't dependent on any external storage, so
+ * nothing should be done here.
+ */
slot->tts_ops->materialize(slot);
}
@@ -494,6 +530,30 @@ ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
dstslot->tts_ops->copyslot(dstslot, srcslot);
+ /* Assert this assumption the below code depends on. */
+ Assert(dstslot->tts_nvalid == 0 ||
+ dstslot->tts_nvalid == srcslot->tts_nvalid);
+
+ if (dstslot->tts_nvalid == srcslot->tts_nvalid
+ && !bms_is_empty(srcslot->pre_detoasted_attrs))
+ {
+ int attnum = -1;
+ MemoryContext old = MemoryContextSwitchTo(dstslot->tts_mcxt);
+
+ dstslot->pre_detoasted_attrs = bms_copy(srcslot->pre_detoasted_attrs);
+
+ while ((attnum = bms_next_member(dstslot->pre_detoasted_attrs, attnum)) >= 0)
+ {
+ struct varlena *datum = (struct varlena *) srcslot->tts_values[attnum];
+ Size len;
+
+ Assert(!VARATT_IS_EXTENDED(datum));
+ len = VARSIZE(datum);
+ dstslot->tts_values[attnum] = (Datum) palloc(len);
+ memcpy((void *) dstslot->tts_values[attnum], datum, len);
+ }
+ MemoryContextSwitchTo(old);
+ }
return dstslot;
}
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index efc8890ce6..e91f334254 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -113,6 +113,7 @@ extern Bitmapset *bms_add_range(Bitmapset *a, int lower, int upper);
extern Bitmapset *bms_int_members(Bitmapset *a, const Bitmapset *b);
extern Bitmapset *bms_del_members(Bitmapset *a, const Bitmapset *b);
extern Bitmapset *bms_join(Bitmapset *a, Bitmapset *b);
+extern void bms_zero(Bitmapset *a);
/* support for iterating through the integer elements of a set: */
extern int bms_next_member(const Bitmapset *a, int prevbit);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 561fdd98f1..1a01d480ce 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1474,6 +1474,7 @@ typedef struct ScanState
Relation ss_currentRelation;
struct TableScanDescData *ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
+ Bitmapset *scan_pre_detoast_attrs;
} ScanState;
/* ----------------
@@ -2003,6 +2004,8 @@ typedef struct JoinState
bool single_match; /* True if we should skip to next outer tuple
* after finding one inner match */
ExprState *joinqual; /* JOIN quals (in addition to ps.qual) */
+ Bitmapset *outer_pre_detoast_attrs;
+ Bitmapset *inner_pre_detoast_attrs;
} JoinState;
/* ----------------
@@ -2764,4 +2767,6 @@ typedef struct LimitState
TupleTableSlot *last_slot; /* slot for evaluation of ties */
} LimitState;
+extern void SetPredetoastAttrsForJoin(JoinState *joinstate);
+extern void SetPredetoastAttrsForScan(ScanState *scanstate);
#endif /* EXECNODES_H */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index b4ef6bc44c..2f2fec2740 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -169,6 +169,13 @@ typedef struct Plan
*/
Bitmapset *extParam;
Bitmapset *allParam;
+
+ /*
+ * A list of Vars which should not apply the shared-detoast-datum logic
+ * since the upper nodes like Sort/Hash want the tuples as small as
+ * possible. Its a subset of targetlist in each Plan node.
+ */
+ List *forbid_pre_detoast_vars;
} Plan;
/* ----------------
@@ -385,6 +392,13 @@ typedef struct Scan
Plan plan;
Index scanrelid; /* relid is index into the range table */
+
+ /*
+ * Records of var's varattno - 1 where the Var is accessed indirectly by
+ * any expression, like a > 3. However a IS [NOT] NULL is not included
+ * since it doesn't access the tts_values[*] at all.
+ */
+ Bitmapset *reference_attrs;
} Scan;
/* ----------------
@@ -789,6 +803,14 @@ typedef struct Join
JoinType jointype;
bool inner_unique;
List *joinqual; /* JOIN quals (in addition to plan.qual) */
+
+ /*
+ * Records of var's varattno - 1 where the Var is accessed indirectly by
+ * any expression, like a > 3. However a IS [NOT] NULL is not included
+ * since it doesn't access the tts_values[*] at all.
+ */
+ Bitmapset *outer_reference_attrs;
+ Bitmapset *inner_reference_attrs;
} Join;
/* ----------------
@@ -869,6 +891,14 @@ typedef struct HashJoin
* perform lookups in the hashtable over the inner plan.
*/
List *hashkeys;
+
+ /*
+ * keep the small tlist information in plan for the shared-detoast datum
+ * logic. If left_small_tlist is true, then all the datum in outerPlan
+ * should not apply that logic, used for maintaining
+ * forbid_pre_detoast_vars fields in Plan.
+ */
+ bool left_small_tlist;
} HashJoin;
/* ----------------
@@ -1588,4 +1618,24 @@ typedef enum MonotonicFunction
MONOTONICFUNC_BOTH = MONOTONICFUNC_INCREASING | MONOTONICFUNC_DECREASING,
} MonotonicFunction;
+static inline bool
+is_join_plan(Plan *plan)
+{
+ return (plan != NULL) && (IsA(plan, NestLoop) || IsA(plan, HashJoin) || IsA(plan, MergeJoin));
+}
+
+static inline bool
+is_scan_plan(Plan *plan)
+{
+ return (plan != NULL) &&
+ (IsA(plan, SeqScan) ||
+ IsA(plan, SampleScan) ||
+ IsA(plan, IndexScan) ||
+ IsA(plan, IndexOnlyScan) ||
+ IsA(plan, BitmapIndexScan) ||
+ IsA(plan, BitmapHeapScan) ||
+ IsA(plan, TidScan) ||
+ IsA(plan, SubqueryScan));
+}
+
#endif /* PLANNODES_H */
diff --git a/src/test/regress/sql/shared_detoast_slow.sql b/src/test/regress/sql/shared_detoast_slow.sql
new file mode 100644
index 0000000000..beecaa6bc5
--- /dev/null
+++ b/src/test/regress/sql/shared_detoast_slow.sql
@@ -0,0 +1,70 @@
+create table t1(a text, b text, c text);
+create table t2(a text, b text, c text);
+create table t3(a text, b text, c text);
+
+insert into t1 select i, i, i from generate_series(1, 1000000)i;
+insert into t2 select i, i, i from generate_series(1, 1000000)i;
+insert into t3 select i, i, i from generate_series(1, 1000000)i;
+
+create index on t1(c);
+
+analyze t1;
+analyze t2;
+analyze t3;
+
+-- Turn off jit first, reasons:
+-- 1. JIT is not adapted for this feature, it may cause crash on jit.
+-- 2. more logging for this feature is enabled when jit=off
+set jit to off;
+
+explain (verbose) select * from t1 where b > 'a';
+
+
+-- NullTest has nothing with tts_values, so its access to toast value
+-- should be ignored.
+explain (verbose) select * from t1 where b is NULL and c is not null;
+
+-- b can't be shared-detoasted since it would make the work_mem bigger.
+explain (verbose) select * from t1 where b > 'a' order by c;
+
+-- b CAN be shared-detoasted since it would NOT make the work_mem bigger.
+-- but compared with the old behavior, it cause the lifespan of the
+-- 'detoast datum'
+-- longer, in the old behavior, it is reset becase of ExecQualAndReset.
+explain (verbose) select a, c from t1 where b > 'a' order by c;
+
+-- The detoast only happen at the join stage.
+explain (verbose) select * from t1 join t2 using(b);
+
+--
+explain (verbose) select * from t1 join t2 using(b) where t1.c > '3';
+
+explain (verbose)
+select t3.*
+from t1, t2, t3
+where t2.c > '999999999999999'
+and t2.c = t1.c
+and t3.b = t1.b;
+
+
+-- t2.b the innerHash can't be pre detoast due to Hash.
+-- t1.b the outerPlan can't be pre detoast due to num_batch.
+explain (verbose) select * from t1 join t2 using(b);
+
+-- Increase the work_mem so that the num_batch = 1, then
+-- the t1.b in outerPlan can be pre-detoast.
+
+set work_mem to '1GB';
+explain (verbose) select * from t1 join t2 using(b);
+reset work_mem;
+
+-- Show even if a datum is under a hash node, but it is
+-- not DIRECTLY input of the Hash, it's still able to be
+-- pre detoasted.
+explain (verbose)
+select t3.*
+from t1, t2, t3
+where t2.c > '999999999999999'
+and t2.c = t1.c
+and t3.b = t1.b
+and t1.b > '0';
--
2.34.1
^ permalink raw reply [nested|flat] 11+ messages in thread
* Re: Shared detoast Datum proposal
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-06 15:12 ` Re: Shared detoast Datum proposal vignesh C <[email protected]>
2024-01-07 01:10 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-08 09:52 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
@ 2024-01-22 06:41 ` Peter Smith <[email protected]>
2024-01-23 05:44 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
0 siblings, 1 reply; 11+ messages in thread
From: Peter Smith @ 2024-01-22 06:41 UTC (permalink / raw)
To: Andy Fan <[email protected]>; +Cc: vignesh C <[email protected]>; [email protected] <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>
2024-01 Commitfest.
Hi, This patch has a CF status of "Needs Review" [1], but it seems
there were CFbot test failures last time it was run [2]. Please have a
look and post an updated version if necessary.
======
[1] https://commitfest.postgresql.org/46/4759/
[2] https://cirrus-ci.com/github/postgresql-cfbot/postgresql/commitfest/46/4759
Kind Regards,
Peter Smith.
^ permalink raw reply [nested|flat] 11+ messages in thread
* Re: Shared detoast Datum proposal
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-06 15:12 ` Re: Shared detoast Datum proposal vignesh C <[email protected]>
2024-01-07 01:10 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-08 09:52 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-22 06:41 ` Re: Shared detoast Datum proposal Peter Smith <[email protected]>
@ 2024-01-23 05:44 ` Andy Fan <[email protected]>
2024-01-23 08:59 ` Re: Shared detoast Datum proposal Michael Zhilin <[email protected]>
0 siblings, 1 reply; 11+ messages in thread
From: Andy Fan @ 2024-01-23 05:44 UTC (permalink / raw)
To: Peter Smith <[email protected]>; +Cc: vignesh C <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>; [email protected]
Hi,
Peter Smith <[email protected]> writes:
> 2024-01 Commitfest.
>
> Hi, This patch has a CF status of "Needs Review" [1], but it seems
> there were CFbot test failures last time it was run [2]. Please have a
> look and post an updated version if necessary.
>
> ======
> [1] https://commitfest.postgresql.org/46/4759/
> [2] https://cirrus-ci.com/github/postgresql-cfbot/postgresql/commitfest/46/4759
v5 attached, it should fix the above issue. This version also introduce
a data struct called bitset, which has a similar APIs like bitmapset but
have the ability to reset all bits without recycle its allocated memory,
this is important for this feature.
commit 44754fb03accb0dec9710a962a334ee73eba3c49 (HEAD -> shared_detoast_value_v2)
Author: yizhi.fzh <[email protected]>
Date: Tue Jan 23 13:38:34 2024 +0800
shared detoast feature.
commit 14a6eafef9ff4926b8b877d694de476657deee8a
Author: yizhi.fzh <[email protected]>
Date: Mon Jan 22 15:48:33 2024 +0800
Introduce a Bitset data struct.
While Bitmapset is designed for variable-length of bits, Bitset is
designed for fixed-length of bits, the fixed length must be specified at
the bitset_init stage and keep unchanged at the whole lifespan. Because
of this, some operations on Bitset is simpler than Bitmapset.
The bitset_clear unsets all the bits but kept the allocated memory, this
capacity is impossible for bit Bitmapset for some solid reasons and this
is the main reason to add this data struct.
Also for performance aspect, the functions for Bitset removed some
unlikely checks, instead with some Asserts.
[1] https://postgr.es/m/CAApHDvpdp9LyAoMXvS7iCX-t3VonQM3fTWCmhconEvORrQ%2BZYA%40mail.gmail.com
[2] https://postgr.es/m/875xzqxbv5.fsf%40163.com
I didn't write a good commit message for commit 2, the people who is
interested with this can see the first message in this thread for
explaination.
I think anyone whose customer uses lots of jsonb probably can get
benefits from this. the precondition is the toast value should be
accessed 1+ times, including the jsonb_out function. I think this would
be not rare to happen.
--
Best Regards
Andy Fan
^ permalink raw reply [nested|flat] 11+ messages in thread
* Re: Shared detoast Datum proposal
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-06 15:12 ` Re: Shared detoast Datum proposal vignesh C <[email protected]>
2024-01-07 01:10 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-08 09:52 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-22 06:41 ` Re: Shared detoast Datum proposal Peter Smith <[email protected]>
2024-01-23 05:44 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
@ 2024-01-23 08:59 ` Michael Zhilin <[email protected]>
2024-01-23 19:18 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
0 siblings, 1 reply; 11+ messages in thread
From: Michael Zhilin @ 2024-01-23 08:59 UTC (permalink / raw)
To: Andy Fan <[email protected]>; Peter Smith <[email protected]>; +Cc: vignesh C <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>; [email protected]
Hi Andy,
It looks like v5 is missing in your mail. Could you please check and
resend it?
Thanks,
 Michael.
On 1/23/24 08:44, Andy Fan wrote:
> Hi,
>
> Peter Smith<[email protected]> writes:
>
>> 2024-01 Commitfest.
>>
>> Hi, This patch has a CF status of "Needs Review" [1], but it seems
>> there were CFbot test failures last time it was run [2]. Please have a
>> look and post an updated version if necessary.
>>
>> ======
>> [1]https://commitfest.postgresql.org/46/4759/
>> [2]https://cirrus-ci.com/github/postgresql-cfbot/postgresql/commitfest/46/4759
> v5 attached, it should fix the above issue. This version also introduce
> a data struct called bitset, which has a similar APIs like bitmapset but
> have the ability to reset all bits without recycle its allocated memory,
> this is important for this feature.
>
> commit 44754fb03accb0dec9710a962a334ee73eba3c49 (HEAD -> shared_detoast_value_v2)
> Author: yizhi.fzh<[email protected]>
> Date: Tue Jan 23 13:38:34 2024 +0800
>
> shared detoast feature.
>
> commit 14a6eafef9ff4926b8b877d694de476657deee8a
> Author: yizhi.fzh<[email protected]>
> Date: Mon Jan 22 15:48:33 2024 +0800
>
> Introduce a Bitset data struct.
>
> While Bitmapset is designed for variable-length of bits, Bitset is
> designed for fixed-length of bits, the fixed length must be specified at
> the bitset_init stage and keep unchanged at the whole lifespan. Because
> of this, some operations on Bitset is simpler than Bitmapset.
>
> The bitset_clear unsets all the bits but kept the allocated memory, this
> capacity is impossible for bit Bitmapset for some solid reasons and this
> is the main reason to add this data struct.
>
> Also for performance aspect, the functions for Bitset removed some
> unlikely checks, instead with some Asserts.
>
> [1]https://postgr.es/m/CAApHDvpdp9LyAoMXvS7iCX-t3VonQM3fTWCmhconEvORrQ%2BZYA%40mail.gmail.com
> [2]https://postgr.es/m/875xzqxbv5.fsf%40163.com
>
>
> I didn't write a good commit message for commit 2, the people who is
> interested with this can see the first message in this thread for
> explaination.
>
> I think anyone whose customer uses lots of jsonb probably can get
> benefits from this. the precondition is the toast value should be
> accessed 1+ times, including the jsonb_out function. I think this would
> be not rare to happen.
>
--
Michael Zhilin
Postgres Professional
+7(925)3366270
https://www.postgrespro.ru
^ permalink raw reply [nested|flat] 11+ messages in thread
* Re: Shared detoast Datum proposal
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-06 15:12 ` Re: Shared detoast Datum proposal vignesh C <[email protected]>
2024-01-07 01:10 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-08 09:52 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-22 06:41 ` Re: Shared detoast Datum proposal Peter Smith <[email protected]>
2024-01-23 05:44 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-23 08:59 ` Re: Shared detoast Datum proposal Michael Zhilin <[email protected]>
@ 2024-01-23 19:18 ` Andy Fan <[email protected]>
2024-02-20 06:20 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
0 siblings, 1 reply; 11+ messages in thread
From: Andy Fan @ 2024-01-23 19:18 UTC (permalink / raw)
To: Michael Zhilin <[email protected]>; +Cc: Peter Smith <[email protected]>; vignesh C <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>; [email protected]
Michael Zhilin <[email protected]> writes:
> Hi Andy,
>
> It looks like v5 is missing in your mail. Could you please check and resend it?
ha, yes.. v5 is really attached this time.
commit eee0b2058f912d0d56282711c5d88bc0b1b75c2f (HEAD -> shared_detoast_value_v3)
Author: yizhi.fzh <[email protected]>
Date: Tue Jan 23 13:38:34 2024 +0800
shared detoast feature.
details at https://postgr.es/m/87il4jrk1l.fsf%40163.com
commit eeca405f5ae87e7d4e5496de989ac7b5173bcaa9
Author: yizhi.fzh <[email protected]>
Date: Mon Jan 22 15:48:33 2024 +0800
Introduce a Bitset data struct.
While Bitmapset is designed for variable-length of bits, Bitset is
designed for fixed-length of bits, the fixed length must be specified at
the bitset_init stage and keep unchanged at the whole lifespan. Because
of this, some operations on Bitset is simpler than Bitmapset.
The bitset_clear unsets all the bits but kept the allocated memory, this
capacity is impossible for bit Bitmapset for some solid reasons and this
is the main reason to add this data struct.
Also for performance aspect, the functions for Bitset removed some
unlikely checks, instead with some Asserts.
[1] https://postgr.es/m/CAApHDvpdp9LyAoMXvS7iCX-t3VonQM3fTWCmhconEvORrQ%2BZYA%40mail.gmail.com
[2] https://postgr.es/m/875xzqxbv5.fsf%40163.com
As for the commit "Introduce a Bitset data struct.", the test coverage
is 100% now. So it would be great that people can review this first.
--
Best Regards
Andy Fan
^ permalink raw reply [nested|flat] 11+ messages in thread
* Re: Shared detoast Datum proposal
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-06 15:12 ` Re: Shared detoast Datum proposal vignesh C <[email protected]>
2024-01-07 01:10 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-08 09:52 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-22 06:41 ` Re: Shared detoast Datum proposal Peter Smith <[email protected]>
2024-01-23 05:44 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-23 08:59 ` Re: Shared detoast Datum proposal Michael Zhilin <[email protected]>
2024-01-23 19:18 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
@ 2024-02-20 06:20 ` Andy Fan <[email protected]>
2024-02-20 18:15 ` Re: Shared detoast Datum proposal Tomas Vondra <[email protected]>
0 siblings, 1 reply; 11+ messages in thread
From: Andy Fan @ 2024-02-20 06:20 UTC (permalink / raw)
To: Andy Fan <[email protected]>; +Cc: Michael Zhilin <[email protected]>; Peter Smith <[email protected]>; vignesh C <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>; [email protected]
Hi,
I didn't another round of self-review. Comments, variable names, the
order of function definition are improved so that it can be read as
smooth as possible. so v6 attached.
--
Best Regards
Andy Fan
Attachments:
[text/x-diff] v6-0001-Introduce-a-Bitset-data-struct.patch (16.5K, ../../[email protected]/2-v6-0001-Introduce-a-Bitset-data-struct.patch)
download | inline diff:
From f2e7772228e8a18027b9c29f10caba9c6570d934 Mon Sep 17 00:00:00 2001
From: "yizhi.fzh" <[email protected]>
Date: Tue, 20 Feb 2024 11:11:53 +0800
Subject: [PATCH v6 1/2] Introduce a Bitset data struct.
While Bitmapset is designed for variable-length of bits, Bitset is
designed for fixed-length of bits, the fixed length must be specified at
the bitset_init stage and keep unchanged at the whole lifespan. Because
of this, some operations on Bitset is simpler than Bitmapset.
The bitset_clear unsets all the bits but kept the allocated memory, this
capacity is impossible for bit Bitmapset for some solid reasons and this
is the main reason to add this data struct.
[1] https://postgr.es/m/CAApHDvpdp9LyAoMXvS7iCX-t3VonQM3fTWCmhconEvORrQ%2BZYA%40mail.gmail.com
[2] https://postgr.es/m/875xzqxbv5.fsf%40163.com
---
src/backend/nodes/bitmapset.c | 200 +++++++++++++++++-
src/backend/nodes/outfuncs.c | 51 +++++
src/include/nodes/bitmapset.h | 28 +++
src/include/nodes/nodes.h | 4 +
src/test/modules/test_misc/Makefile | 11 +
src/test/modules/test_misc/README | 4 +-
.../test_misc/expected/test_bitset.out | 7 +
src/test/modules/test_misc/meson.build | 17 ++
.../modules/test_misc/sql/test_bitset.sql | 3 +
src/test/modules/test_misc/test_misc--1.0.sql | 5 +
src/test/modules/test_misc/test_misc.c | 118 +++++++++++
src/test/modules/test_misc/test_misc.control | 4 +
src/tools/pgindent/typedefs.list | 1 +
13 files changed, 441 insertions(+), 12 deletions(-)
create mode 100644 src/test/modules/test_misc/expected/test_bitset.out
create mode 100644 src/test/modules/test_misc/sql/test_bitset.sql
create mode 100644 src/test/modules/test_misc/test_misc--1.0.sql
create mode 100644 src/test/modules/test_misc/test_misc.c
create mode 100644 src/test/modules/test_misc/test_misc.control
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 65805d4527..40cfea2308 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -1315,23 +1315,18 @@ bms_join(Bitmapset *a, Bitmapset *b)
* It makes no difference in simple loop usage, but complex iteration logic
* might need such an ability.
*/
-int
-bms_next_member(const Bitmapset *a, int prevbit)
+
+static int
+bms_next_member_internal(int nwords, const bitmapword *words, int prevbit)
{
- int nwords;
int wordnum;
bitmapword mask;
- Assert(bms_is_valid_set(a));
-
- if (a == NULL)
- return -2;
- nwords = a->nwords;
prevbit++;
mask = (~(bitmapword) 0) << BITNUM(prevbit);
for (wordnum = WORDNUM(prevbit); wordnum < nwords; wordnum++)
{
- bitmapword w = a->words[wordnum];
+ bitmapword w = words[wordnum];
/* ignore bits before prevbit */
w &= mask;
@@ -1351,6 +1346,19 @@ bms_next_member(const Bitmapset *a, int prevbit)
return -2;
}
+int
+bms_next_member(const Bitmapset *a, int prevbit)
+{
+ Assert(a == NULL || IsA(a, Bitmapset));
+
+ Assert(bms_is_valid_set(a));
+
+ if (a == NULL)
+ return -2;
+
+ return bms_next_member_internal(a->nwords, a->words, prevbit);
+}
+
/*
* bms_prev_member - find prev member of a set
*
@@ -1458,3 +1466,177 @@ bitmap_match(const void *key1, const void *key2, Size keysize)
return !bms_equal(*((const Bitmapset *const *) key1),
*((const Bitmapset *const *) key2));
}
+
+/*
+ * bitset_init - create a Bitset. the set will be round up to nwords;
+ */
+Bitset *
+bitset_init(size_t size)
+{
+ int nword = (size + BITS_PER_BITMAPWORD - 1) / BITS_PER_BITMAPWORD;
+ Bitset *result;
+
+ if (size == 0)
+ return NULL;
+
+ result = (Bitset *) palloc0(sizeof(Bitset) + nword * sizeof(bitmapword));
+ result->nwords = nword;
+
+ return result;
+}
+
+/*
+ * bitset_clear - clear the bits only, but the memory is still there.
+ */
+void
+bitset_clear(Bitset *a)
+{
+ if (a != NULL)
+ memset(a->words, 0, sizeof(bitmapword) * a->nwords);
+}
+
+void
+bitset_free(Bitset *a)
+{
+ if (a != NULL)
+ pfree(a);
+}
+
+bool
+bitset_is_empty(Bitset *a)
+{
+ int i;
+
+ if (a == NULL)
+ return true;
+
+ for (i = 0; i < a->nwords; i++)
+ {
+ bitmapword w = a->words[i];
+
+ if (w != 0)
+ return false;
+ }
+
+ return true;
+}
+
+Bitset *
+bitset_copy(Bitset *a)
+{
+ Bitset *result;
+
+ if (a == NULL)
+ return NULL;
+
+ result = bitset_init(a->nwords * BITS_PER_BITMAPWORD);
+
+ memcpy(result->words, a->words, sizeof(bitmapword) * a->nwords);
+ return result;
+}
+
+void
+bitset_add_member(Bitset *a, int x)
+{
+ int wordnum,
+ bitnum;
+
+ Assert(x >= 0);
+
+ wordnum = WORDNUM(x);
+ bitnum = BITNUM(x);
+
+ Assert(wordnum < a->nwords);
+
+ a->words[wordnum] |= ((bitmapword) 1 << bitnum);
+}
+
+void
+bitset_del_member(Bitset *a, int x)
+{
+ int wordnum,
+ bitnum;
+
+ Assert(x >= 0);
+
+ wordnum = WORDNUM(x);
+ bitnum = BITNUM(x);
+
+ Assert(wordnum < a->nwords);
+
+ a->words[wordnum] &= ~((bitmapword) 1 << bitnum);
+}
+
+int
+bitset_is_member(int x, Bitset *a)
+{
+ int wordnum,
+ bitnum;
+
+ /* used in expression engine */
+ Assert(x >= 0);
+
+ wordnum = WORDNUM(x);
+ bitnum = BITNUM(x);
+
+ if (a == NULL)
+ return false;
+
+ if (wordnum >= a->nwords)
+ return false;
+
+ return (a->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0;
+}
+
+int
+bitset_next_member(const Bitset *a, int prevbit)
+{
+ if (a == NULL)
+ return -2;
+
+ return bms_next_member_internal(a->nwords, a->words, prevbit);
+}
+
+
+/*
+ * bitset_to_bitmap - build a legal bitmapset from bitset.
+ */
+Bitmapset *
+bitset_to_bitmap(Bitset *a)
+{
+ int n;
+
+ bool found = false; /* any non-empty bits */
+ Bitmapset *result;
+ int i;
+
+ if (a == NULL)
+ return NULL;
+
+ n = a->nwords - 1;
+ do
+ {
+ if (a->words[n] > 0)
+ {
+ found = true;
+ break;
+ }
+ } while (--n >= 0);
+
+ if (!found)
+ return NULL;
+
+ result = (Bitmapset *) palloc0(BITMAPSET_SIZE(n + 1));
+ result->type = T_Bitmapset;
+ result->nwords = n + 1;
+
+ Assert(result->nwords <= a->nwords);
+
+ i = 0;
+ do
+ {
+ result->words[i] = a->words[i];
+ } while (++i < result->nwords);
+
+ return result;
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 25171864db..e144b62418 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -331,6 +331,43 @@ outBitmapset(StringInfo str, const Bitmapset *bms)
appendStringInfoChar(str, ')');
}
+
+
+/*
+ * outBitset -
+ * similar to outBitmapset, but for Bitset.
+ */
+static void
+outBitsetInternal(struct StringInfoData *str,
+ const struct Bitset *bs,
+ bool asBitmap)
+{
+ int x;
+
+ appendStringInfoChar(str, '(');
+ if (asBitmap)
+ appendStringInfoChar(str, 'b');
+ else
+ appendStringInfo(str, "bs");
+ x = -1;
+ while ((x = bitset_next_member(bs, x)) >= 0)
+ appendStringInfo(str, " %d", x);
+ appendStringInfoChar(str, ')');
+}
+
+
+/*
+ * outBitset -
+ * similar to outBitmapset, but for Bitset.
+ */
+void
+outBitset(struct StringInfoData *str,
+ const struct Bitset *bs)
+{
+ outBitsetInternal(str, bs, false);
+}
+
+
/*
* Print the value of a Datum given its type.
*/
@@ -911,3 +948,17 @@ bmsToString(const Bitmapset *bms)
outBitmapset(&str, bms);
return str.data;
}
+
+/*
+ * bitsetToString -
+ * similar to bmsToString, but for Bitset
+ */
+char *
+bitsetToString(const struct Bitset *bs, bool asBitmap)
+{
+ StringInfoData str;
+
+ initStringInfo(&str);
+ outBitsetInternal(&str, bs, asBitmap);
+ return str.data;
+}
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 906e8dcc15..95ff37c6e9 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -55,6 +55,24 @@ typedef struct Bitmapset
bitmapword words[FLEXIBLE_ARRAY_MEMBER]; /* really [nwords] */
} Bitmapset;
+/*
+ * While Bitmapset is designed for variable-length of bits, Bitset is
+ * designed for fixed-length of bits, the fixed length must be specified at
+ * the bitset_init stage and keep unchanged at the whole lifespan. Because
+ * of this, some operations on Bitset is simpler than Bitmapset.
+ *
+ * The bitset_clear unsets all the bits but kept the allocated memory, this
+ * capacity is impossible for bit Bitmapset for some solid reasons.
+ *
+ * Also for performance aspect, the functions for Bitset removed some
+ * unlikely checks, instead with some Asserts.
+ */
+
+typedef struct Bitset
+{
+ int nwords; /* number of words in array */
+ bitmapword words[FLEXIBLE_ARRAY_MEMBER]; /* really [nwords] */
+} Bitset;
/* result of bms_subset_compare */
typedef enum
@@ -124,4 +142,14 @@ extern uint32 bms_hash_value(const Bitmapset *a);
extern uint32 bitmap_hash(const void *key, Size keysize);
extern int bitmap_match(const void *key1, const void *key2, Size keysize);
+extern Bitset *bitset_init(size_t size);
+extern void bitset_clear(Bitset *a);
+extern void bitset_free(Bitset *a);
+extern bool bitset_is_empty(Bitset *a);
+extern Bitset *bitset_copy(Bitset *a);
+extern void bitset_add_member(Bitset *a, int x);
+extern void bitset_del_member(Bitset *a, int x);
+extern int bitset_is_member(int bit, Bitset *a);
+extern int bitset_next_member(const Bitset *a, int prevbit);
+extern Bitmapset *bitset_to_bitmap(Bitset *a);
#endif /* BITMAPSET_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 2969dd831b..4d13107990 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -186,16 +186,20 @@ castNodeImpl(NodeTag type, void *ptr)
* nodes/{outfuncs.c,print.c}
*/
struct Bitmapset; /* not to include bitmapset.h here */
+struct Bitset; /* not to include bitmapset.h here */
struct StringInfoData; /* not to include stringinfo.h here */
extern void outNode(struct StringInfoData *str, const void *obj);
extern void outToken(struct StringInfoData *str, const char *s);
extern void outBitmapset(struct StringInfoData *str,
const struct Bitmapset *bms);
+extern void outBitset(struct StringInfoData *str, const struct Bitset *bs);
+
extern void outDatum(struct StringInfoData *str, uintptr_t value,
int typlen, bool typbyval);
extern char *nodeToString(const void *obj);
extern char *bmsToString(const struct Bitmapset *bms);
+extern char *bitsetToString(const struct Bitset *bs, bool asBitmap);
/*
* nodes/{readfuncs.c,read.c}
diff --git a/src/test/modules/test_misc/Makefile b/src/test/modules/test_misc/Makefile
index 39c6c2014a..af96604096 100644
--- a/src/test/modules/test_misc/Makefile
+++ b/src/test/modules/test_misc/Makefile
@@ -2,6 +2,17 @@
TAP_TESTS = 1
+MODULE_big = test_misc
+OBJS = \
+ $(WIN32RES) \
+ test_misc.o
+PGFILEDESC = "test_misc"
+
+EXTENSION = test_misc
+DATA = test_misc--1.0.sql
+
+REGRESS = test_bitset
+
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
diff --git a/src/test/modules/test_misc/README b/src/test/modules/test_misc/README
index 4876733fa2..ec426c4ad5 100644
--- a/src/test/modules/test_misc/README
+++ b/src/test/modules/test_misc/README
@@ -1,4 +1,2 @@
-This directory doesn't actually contain any extension module.
-
-What it is is a home for otherwise-unclassified TAP tests that exercise core
+What it is is a home for otherwise-unclassified tests that exercise core
server features. We might equally well have called it, say, src/test/misc.
diff --git a/src/test/modules/test_misc/expected/test_bitset.out b/src/test/modules/test_misc/expected/test_bitset.out
new file mode 100644
index 0000000000..3d0302d30d
--- /dev/null
+++ b/src/test/modules/test_misc/expected/test_bitset.out
@@ -0,0 +1,7 @@
+CREATE EXTENSION test_misc;
+SELECT test_bitset();
+ test_bitset
+-------------
+
+(1 row)
+
diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build
index 964d95db26..a23f3e3f47 100644
--- a/src/test/modules/test_misc/meson.build
+++ b/src/test/modules/test_misc/meson.build
@@ -1,5 +1,22 @@
# Copyright (c) 2022-2024, PostgreSQL Global Development Group
+test_misc_sources = files(
+ 'test_misc.c',
+)
+
+if host_system == 'windows'
+ test_misc_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'test_misc',
+ '--FILEDESC', 'test_misc - ',])
+endif
+
+test_misc = shared_module('test_misc',
+ test_misc_sources,
+ kwargs: pg_test_mod_args,
+)
+
+test_install_libs += test_misc
+
tests += {
'name': 'test_misc',
'sd': meson.current_source_dir(),
diff --git a/src/test/modules/test_misc/sql/test_bitset.sql b/src/test/modules/test_misc/sql/test_bitset.sql
new file mode 100644
index 0000000000..0f73bbf532
--- /dev/null
+++ b/src/test/modules/test_misc/sql/test_bitset.sql
@@ -0,0 +1,3 @@
+CREATE EXTENSION test_misc;
+
+SELECT test_bitset();
diff --git a/src/test/modules/test_misc/test_misc--1.0.sql b/src/test/modules/test_misc/test_misc--1.0.sql
new file mode 100644
index 0000000000..79afaa6263
--- /dev/null
+++ b/src/test/modules/test_misc/test_misc--1.0.sql
@@ -0,0 +1,5 @@
+\echo Use "CREATE EXTENSION test_misc" to load this file. \quit
+
+CREATE FUNCTION test_bitset()
+ RETURNS pg_catalog.void
+ AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/src/test/modules/test_misc/test_misc.c b/src/test/modules/test_misc/test_misc.c
new file mode 100644
index 0000000000..70d0255ada
--- /dev/null
+++ b/src/test/modules/test_misc/test_misc.c
@@ -0,0 +1,118 @@
+/*--------------------------------------------------------------------------
+ *
+ * test_misc.c
+ *
+ * Copyright (c) 2022-2024, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/test/modules/test_dsa/test_misc.c
+ *
+ * -------------------------------------------------------------------------
+ */
+#include "postgres.h"
+#include "fmgr.h"
+#include "nodes/bitmapset.h"
+#include "nodes/nodes.h"
+#define BIT_ADD 0
+#define BIT_DEL 1
+#ifdef USE_ASSERT_CHECKING
+static void compare_bms_bs(Bitmapset **bms, Bitset *bs, int member, int op);
+#endif
+PG_MODULE_MAGIC;
+/* Test basic DSA functionality */
+PG_FUNCTION_INFO_V1(test_bitset);
+Datum
+test_bitset(PG_FUNCTION_ARGS)
+{
+#ifdef USE_ASSERT_CHECKING
+ Bitset *bs;
+ Bitset *bs2;
+ char *str1,
+ *str2,
+ *empty_str;
+ Bitmapset *bms = NULL;
+ int i;
+
+ empty_str = bmsToString(NULL);
+ /* size = 0 */
+ bs = bitset_init(0);
+ Assert(bs == NULL);
+ bitset_clear(bs);
+ Assert(bitset_is_empty(bs));
+ /* bitset_add_member(bs, 0); // crash. */
+ /* bitset_del_member(bs, 0); // crash. */
+ Assert(!bitset_is_member(0, bs));
+ Assert(bitset_next_member(bs, -1) == -2);
+ bs2 = bitset_copy(bs);
+ Assert(bs2 == NULL);
+ bitset_free(bs);
+ bitset_free(bs2);
+ /* size == 68, nword == 2 */
+ bs = bitset_init(68);
+ for (i = 0; i < 68; i = i + 3)
+ {
+ compare_bms_bs(&bms, bs, i, BIT_ADD);
+ }
+ Assert(!bitset_is_empty(bs));
+ for (i = 0; i < 68; i = i + 3)
+ {
+ compare_bms_bs(&bms, bs, i, BIT_DEL);
+ }
+ Assert(bitset_is_empty(bs));
+ bitset_clear(bs);
+ str1 = bitsetToString(bs, true);
+ Assert(strcmp(str1, empty_str) == 0);
+ bms = bitset_to_bitmap(bs);
+ str2 = bmsToString(bms);
+ Assert(strcmp(str1, str2) == 0);
+ bms = bitset_to_bitmap(NULL);
+ Assert(strcmp(bmsToString(bms), empty_str) == 0);
+ bitset_free(bs);
+#endif
+ PG_RETURN_VOID();
+}
+#ifdef USE_ASSERT_CHECKING
+static void
+compare_bms_bs(Bitmapset **bms, Bitset *bs, int member, int op)
+{
+ char *str1,
+ *str2,
+ *str3,
+ *str4;
+ Bitmapset *bms3;
+ Bitset *bs4;
+
+ if (op == BIT_ADD)
+ {
+ *bms = bms_add_member(*bms, member);
+ bitset_add_member(bs, member);
+ Assert(bms_is_member(member, *bms));
+ Assert(bitset_is_member(member, bs));
+ }
+ else if (op == BIT_DEL)
+ {
+ *bms = bms_del_member(*bms, member);
+ bitset_del_member(bs, member);
+ Assert(!bms_is_member(member, *bms));
+ Assert(!bitset_is_member(member, bs));
+ }
+ else
+ Assert(false);
+ /* compare the rest existing bit */
+ str1 = bmsToString(*bms);
+ str2 = bitsetToString(bs, true);
+ Assert(strcmp(str1, str2) == 0);
+ /* test bitset_to_bitmap */
+ bms3 = bitset_to_bitmap(bs);
+ str3 = bmsToString(bms3);
+ Assert(strcmp(str3, str2) == 0);
+ /* test bitset_copy */
+ bs4 = bitset_copy(bs);
+ str4 = bitsetToString(bs4, true);
+ Assert(strcmp(str3, str4) == 0);
+ pfree(str1);
+ pfree(str2);
+ pfree(str3);
+ pfree(str4);
+}
+#endif
diff --git a/src/test/modules/test_misc/test_misc.control b/src/test/modules/test_misc/test_misc.control
new file mode 100644
index 0000000000..48fd08758f
--- /dev/null
+++ b/src/test/modules/test_misc/test_misc.control
@@ -0,0 +1,4 @@
+comment = 'Test misc'
+default_version = '1.0'
+module_pathname = '$libdir/test_misc'
+relocatable = true
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index d808aad8b0..dcba329ee4 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -268,6 +268,7 @@ BitmapOr
BitmapOrPath
BitmapOrState
Bitmapset
+Bitset
Block
BlockId
BlockIdData
--
2.34.1
[text/x-diff] v6-0002-Shared-detoast-feature.patch (73.9K, ../../[email protected]/3-v6-0002-Shared-detoast-feature.patch)
download | inline diff:
From 1b169cf256efe94cea48ef20fa0bd2bffa9e2368 Mon Sep 17 00:00:00 2001
From: "yizhi.fzh" <[email protected]>
Date: Tue, 20 Feb 2024 14:16:10 +0800
Subject: [PATCH v6 2/2] Shared detoast feature.
details at https://postgr.es/m/87il4jrk1l.fsf%40163.com
---
src/backend/executor/execExpr.c | 35 +-
src/backend/executor/execExprInterp.c | 180 ++++++++
src/backend/executor/execTuples.c | 134 ++++++
src/backend/executor/execUtils.c | 2 +
src/backend/executor/nodeHashjoin.c | 2 +
src/backend/executor/nodeMergejoin.c | 2 +
src/backend/executor/nodeNestloop.c | 1 +
src/backend/jit/llvm/llvmjit_expr.c | 26 +-
src/backend/jit/llvm/llvmjit_types.c | 1 +
src/backend/optimizer/plan/createplan.c | 103 ++++-
src/backend/optimizer/plan/setrefs.c | 550 +++++++++++++++++++-----
src/include/executor/execExpr.h | 12 +
src/include/executor/tuptable.h | 60 +++
src/include/nodes/execnodes.h | 14 +
src/include/nodes/plannodes.h | 53 +++
src/tools/pgindent/typedefs.list | 2 +
16 files changed, 1071 insertions(+), 106 deletions(-)
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 3181b1136a..779fcfaab1 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -932,22 +932,51 @@ ExecInitExprRec(Expr *node, ExprState *state,
}
else
{
+ int attnum;
+ Plan *plan = state->parent ? state->parent->plan : NULL;
+
/* regular user column */
scratch.d.var.attnum = variable->varattno - 1;
scratch.d.var.vartype = variable->vartype;
+ attnum = scratch.d.var.attnum;
+
switch (variable->varno)
{
case INNER_VAR:
- scratch.opcode = EEOP_INNER_VAR;
+
+ if (is_join_plan(plan) &&
+ bms_is_member(attnum,
+ ((JoinState *) state->parent)->inner_pre_detoast_attrs))
+ {
+ scratch.opcode = EEOP_INNER_VAR_TOAST;
+ }
+ else
+ {
+ scratch.opcode = EEOP_INNER_VAR;
+ }
break;
case OUTER_VAR:
- scratch.opcode = EEOP_OUTER_VAR;
+ if (is_join_plan(plan) &&
+ bms_is_member(attnum,
+ ((JoinState *) state->parent)->outer_pre_detoast_attrs))
+ {
+ scratch.opcode = EEOP_OUTER_VAR_TOAST;
+ }
+ else
+ scratch.opcode = EEOP_OUTER_VAR;
break;
/* INDEX_VAR is handled by default case */
default:
- scratch.opcode = EEOP_SCAN_VAR;
+ if (is_scan_plan(plan) && bms_is_member(
+ attnum,
+ ((ScanState *) state->parent)->scan_pre_detoast_attrs))
+ {
+ scratch.opcode = EEOP_SCAN_VAR_TOAST;
+ }
+ else
+ scratch.opcode = EEOP_SCAN_VAR;
break;
}
}
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 3f20f1dd31..6472fa463a 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -57,6 +57,7 @@
#include "postgres.h"
#include "access/heaptoast.h"
+#include "access/detoast.h"
#include "catalog/pg_type.h"
#include "commands/sequence.h"
#include "executor/execExpr.h"
@@ -158,6 +159,9 @@ static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarToast(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
@@ -166,6 +170,9 @@ static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull
static Datum ExecJustInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull);
@@ -181,6 +188,43 @@ static pg_attribute_always_inline void ExecAggPlainTransByRef(AggState *aggstate
AggStatePerGroup pergroup,
ExprContext *aggcontext,
int setno);
+static inline void
+ExecSlotDetoastDatum(TupleTableSlot *slot, int attnum)
+{
+ if (!slot->tts_isnull[attnum] &&
+ VARATT_IS_EXTENDED(slot->tts_values[attnum]))
+ {
+ Datum oldDatum;
+ MemoryContext old = MemoryContextSwitchTo(slot->tts_mcxt);
+
+ oldDatum = slot->tts_values[attnum];
+ slot->tts_values[attnum] = PointerGetDatum(detoast_attr(
+ (struct varlena *) oldDatum));
+ Assert(slot->tts_nvalid > attnum);
+ if (oldDatum != slot->tts_values[attnum])
+ bitset_add_member(slot->pre_detoasted_attrs, attnum);
+ MemoryContextSwitchTo(old);
+ }
+}
+
+/* JIT requires a non-static (and external?) function */
+void
+ExecSlotDetoastDatumExternal(TupleTableSlot *slot, int attnum)
+{
+ return ExecSlotDetoastDatum(slot, attnum);
+}
+
+
+static inline void
+ExecEvalToastVar(TupleTableSlot *slot,
+ ExprEvalStep *op,
+ int attnum)
+{
+ ExecSlotDetoastDatum(slot, attnum);
+
+ *op->resvalue = slot->tts_values[attnum];
+ *op->resnull = slot->tts_isnull[attnum];
+}
/*
* ScalarArrayOpExprHashEntry
@@ -296,6 +340,24 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustScanVar;
return;
}
+ if (step0 == EEOP_INNER_FETCHSOME &&
+ step1 == EEOP_INNER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustInnerVarToast;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_FETCHSOME &&
+ step1 == EEOP_OUTER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustOuterVarToast;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_FETCHSOME &&
+ step1 == EEOP_SCAN_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustScanVarToast;
+ return;
+ }
else if (step0 == EEOP_INNER_FETCHSOME &&
step1 == EEOP_ASSIGN_INNER_VAR)
{
@@ -346,6 +408,21 @@ ExecReadyInterpretedExpr(ExprState *state)
state->evalfunc_private = (void *) ExecJustScanVarVirt;
return;
}
+ else if (step0 == EEOP_INNER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustInnerVarVirtToast;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustOuterVarVirtToast;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_VAR_TOAST)
+ {
+ state->evalfunc_private = (void *) ExecJustScanVarVirtToast;
+ return;
+ }
else if (step0 == EEOP_ASSIGN_INNER_VAR)
{
state->evalfunc_private = (void *) ExecJustAssignInnerVarVirt;
@@ -413,6 +490,9 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_INNER_VAR,
&&CASE_EEOP_OUTER_VAR,
&&CASE_EEOP_SCAN_VAR,
+ &&CASE_EEOP_INNER_VAR_TOAST,
+ &&CASE_EEOP_OUTER_VAR_TOAST,
+ &&CASE_EEOP_SCAN_VAR_TOAST,
&&CASE_EEOP_INNER_SYSVAR,
&&CASE_EEOP_OUTER_SYSVAR,
&&CASE_EEOP_SCAN_SYSVAR,
@@ -597,6 +677,25 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
*op->resvalue = scanslot->tts_values[attnum];
*op->resnull = scanslot->tts_isnull[attnum];
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_INNER_VAR_TOAST)
+ {
+ ExecEvalToastVar(innerslot, op, op->d.var.attnum);
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_OUTER_VAR_TOAST)
+ {
+ ExecEvalToastVar(outerslot, op, op->d.var.attnum);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SCAN_VAR_TOAST)
+ {
+ ExecEvalToastVar(scanslot, op, op->d.var.attnum);
EEO_NEXT();
}
@@ -2137,6 +2236,42 @@ ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
return ExecJustVarImpl(state, econtext->ecxt_scantuple, isnull);
}
+static pg_attribute_always_inline Datum
+ExecJustVarImplToast(ExprState *state, TupleTableSlot *slot, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum;
+
+ CheckOpSlotCompatibility(&state->steps[0], slot);
+
+ slot_getattr(slot, attnum + 1, isnull);
+
+ ExecSlotDetoastDatum(slot, attnum);
+
+ return slot->tts_values[attnum];
+}
+
+/* Simple reference to inner Var */
+static Datum
+ExecJustInnerVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_innertuple, isnull);
+}
+
+/* Simple reference to outer Var */
+static Datum
+ExecJustOuterVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_outertuple, isnull);
+}
+
+/* Simple reference to scan Var */
+static Datum
+ExecJustScanVarToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarImplToast(state, econtext->ecxt_scantuple, isnull);
+}
+
/* implementation of ExecJustAssign(Inner|Outer|Scan)Var */
static pg_attribute_always_inline Datum
ExecJustAssignVarImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
@@ -2275,6 +2410,51 @@ ExecJustScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull)
return ExecJustVarVirtImpl(state, econtext->ecxt_scantuple, isnull);
}
+/* implementation of ExecJust(Inner|Outer|Scan)VarVirt */
+static pg_attribute_always_inline Datum
+ExecJustVarVirtImplToast(ExprState *state, TupleTableSlot *slot, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[0];
+ int attnum = op->d.var.attnum;
+
+ /*
+ * As it is guaranteed that a virtual slot is used, there never is a need
+ * to perform tuple deforming (nor would it be possible). Therefore
+ * execExpr.c has not emitted an EEOP_*_FETCHSOME step. Verify, as much as
+ * possible, that that determination was accurate.
+ */
+ Assert(TTS_IS_VIRTUAL(slot));
+ Assert(TTS_FIXED(slot));
+ Assert(attnum >= 0 && attnum < slot->tts_nvalid);
+
+ *isnull = slot->tts_isnull[attnum];
+
+ ExecSlotDetoastDatum(slot, attnum);
+
+ return slot->tts_values[attnum];
+}
+
+/* Like ExecJustInnerVar, optimized for virtual slots */
+static Datum
+ExecJustInnerVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_innertuple, isnull);
+}
+
+/* Like ExecJustOuterVar, optimized for virtual slots */
+static Datum
+ExecJustOuterVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_outertuple, isnull);
+}
+
+/* Like ExecJustScanVar, optimized for virtual slots */
+static Datum
+ExecJustScanVarVirtToast(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ return ExecJustVarVirtImplToast(state, econtext->ecxt_scantuple, isnull);
+}
+
/* implementation of ExecJustAssign(Inner|Outer|Scan)VarVirt */
static pg_attribute_always_inline Datum
ExecJustAssignVarVirtImpl(ExprState *state, TupleTableSlot *inslot, bool *isnull)
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index a7aa2ee02b..830e1d4ea6 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -79,6 +79,9 @@ static inline void tts_buffer_heap_store_tuple(TupleTableSlot *slot,
bool transfer_pin);
static void tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree);
+static Bitmapset *cal_final_pre_detoast_attrs(Bitmapset *reference_attrs,
+ TupleDesc tupleDesc,
+ List *forbid_pre_detoast_vars);
const TupleTableSlotOps TTSOpsVirtual;
const TupleTableSlotOps TTSOpsHeapTuple;
@@ -176,6 +179,10 @@ tts_virtual_materialize(TupleTableSlot *slot)
if (att->attbyval || slot->tts_isnull[natt])
continue;
+ if (bitset_is_member(natt, slot->pre_detoasted_attrs))
+ /* it has been in slot->tts_mcxt already. */
+ continue;
+
val = slot->tts_values[natt];
if (att->attlen == -1 &&
@@ -392,6 +399,13 @@ tts_heap_materialize(TupleTableSlot *slot)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
+
+ /*
+ * tts_values is treated invalidated since tts_nvalid is set to 0, so
+ * let's free the pre-detoast datum.
+ */
+ ExecFreePreDetoastDatum(slot);
+
}
static void
@@ -457,6 +471,9 @@ tts_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple, bool shouldFree)
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /* slot_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
@@ -567,6 +584,9 @@ tts_minimal_materialize(TupleTableSlot *slot)
mslot->minhdr.t_data = (HeapTupleHeader) ((char *) mslot->mintuple - MINIMAL_TUPLE_OFFSET);
MemoryContextSwitchTo(oldContext);
+
+ /* slot_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
static void
@@ -637,6 +657,9 @@ tts_minimal_store_tuple(TupleTableSlot *slot, MinimalTuple mtup, bool shouldFree
if (shouldFree)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
+
+ /* tts_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
@@ -771,6 +794,9 @@ tts_buffer_heap_materialize(TupleTableSlot *slot)
slot->tts_flags |= TTS_FLAG_SHOULDFREE;
MemoryContextSwitchTo(oldContext);
+
+ /* slot_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
static void
@@ -904,6 +930,9 @@ tts_buffer_heap_store_tuple(TupleTableSlot *slot, HeapTuple tuple,
*/
ReleaseBuffer(buffer);
}
+
+ /* tts_nvalid = 0 */
+ ExecFreePreDetoastDatum(slot);
}
/*
@@ -1150,7 +1179,10 @@ MakeTupleTableSlot(TupleDesc tupleDesc,
+ MAXALIGN(tupleDesc->natts * sizeof(Datum)));
PinTupleDesc(tupleDesc);
+ slot->pre_detoasted_attrs = bitset_init(tupleDesc->natts);
}
+ else
+ slot->pre_detoasted_attrs = NULL;
/*
* And allow slot type specific initialization.
@@ -1288,6 +1320,8 @@ void
ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
TupleDesc tupdesc) /* new tuple descriptor */
{
+ MemoryContext old;
+
Assert(!TTS_FIXED(slot));
/* For safety, make sure slot is empty before changing it */
@@ -1304,6 +1338,8 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
pfree(slot->tts_values);
if (slot->tts_isnull)
pfree(slot->tts_isnull);
+ if (slot->pre_detoasted_attrs)
+ bitset_free(slot->pre_detoasted_attrs);
/*
* Install the new descriptor; if it's refcounted, bump its refcount.
@@ -1319,6 +1355,10 @@ ExecSetSlotDescriptor(TupleTableSlot *slot, /* slot to change */
MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(Datum));
slot->tts_isnull = (bool *)
MemoryContextAlloc(slot->tts_mcxt, tupdesc->natts * sizeof(bool));
+
+ old = MemoryContextSwitchTo(slot->tts_mcxt);
+ slot->pre_detoasted_attrs = bitset_init(tupdesc->natts);
+ MemoryContextSwitchTo(old);
}
/* --------------------------------
@@ -1810,12 +1850,26 @@ void
ExecInitScanTupleSlot(EState *estate, ScanState *scanstate,
TupleDesc tupledesc, const TupleTableSlotOps *tts_ops)
{
+ Scan *splan = (Scan *) scanstate->ps.plan;
+
scanstate->ss_ScanTupleSlot = ExecAllocTableSlot(&estate->es_tupleTable,
tupledesc, tts_ops);
scanstate->ps.scandesc = tupledesc;
scanstate->ps.scanopsfixed = tupledesc != NULL;
scanstate->ps.scanops = tts_ops;
scanstate->ps.scanopsset = true;
+
+ if (is_scan_plan((Plan *) splan))
+ {
+ /*
+ * We may run detoast in Qual or Projection, but all of them happen at
+ * the ss_ScanTupleSlot rather than ps_ResultTupleSlot. So we can only
+ * take care of the ss_ScanTupleSlot.
+ */
+ scanstate->scan_pre_detoast_attrs = cal_final_pre_detoast_attrs(splan->reference_attrs,
+ tupledesc,
+ splan->plan.forbid_pre_detoast_vars);
+ }
}
/* ----------------
@@ -2336,3 +2390,83 @@ end_tup_output(TupOutputState *tstate)
ExecDropSingleTupleTableSlot(tstate->slot);
pfree(tstate);
}
+
+/*
+ * cal_final_pre_detoast_attrs
+ * Calculate the final attributes which pre-detoast be helpful.
+ *
+ * reference_attrs: the attributes which will be detoast at this plan level.
+ * due to the implementation issue, some non-toast attribute may be included
+ * which should be filtered out with tupleDesc.
+ *
+ * forbid_pre_detoast_vars: the vars which should not be pre-detoast as the
+ * small_tlist reason.
+ */
+static Bitmapset *
+cal_final_pre_detoast_attrs(Bitmapset *reference_attrs,
+ TupleDesc tupleDesc,
+ List *forbid_pre_detoast_vars)
+{
+ Bitmapset *final = NULL,
+ *toast_attrs = NULL,
+ *forbid_pre_detoast_attrs = NULL;
+
+ int i;
+ ListCell *lc;
+
+ if (bms_is_empty(reference_attrs))
+ return NULL;
+
+ /*
+ * there is no exact data type in create_plan or set_plan_refs stage, so
+ * reference_attrs may have some attribute which is not toast attrs at
+ * all, which should be removed.
+ */
+ for (i = 0; i < tupleDesc->natts; i++)
+ {
+ Form_pg_attribute attr = TupleDescAttr(tupleDesc, i);
+
+ if (attr->attlen == -1 && attr->attstorage != TYPSTORAGE_PLAIN)
+ toast_attrs = bms_add_member(toast_attrs, attr->attnum - 1);
+ }
+
+ /* Filter out the non-toastable attributes. */
+ final = bms_intersect(reference_attrs, toast_attrs);
+
+ /*
+ * Due to the fact of detoast-datum will make the tuple bigger which is
+ * bad for some nodes like Sort/Hash, to avoid performance regression,
+ * such attribute should be removed as well.
+ */
+ foreach(lc, forbid_pre_detoast_vars)
+ {
+ Var *var = lfirst_node(Var, lc);
+
+ forbid_pre_detoast_attrs = bms_add_member(forbid_pre_detoast_attrs, var->varattno - 1);
+ }
+
+ final = bms_del_members(final, forbid_pre_detoast_attrs);
+
+ bms_free(toast_attrs);
+ bms_free(forbid_pre_detoast_attrs);
+
+ return final;
+}
+
+
+void
+SetPredetoastAttrsForJoin(JoinState *j)
+{
+ PlanState *outerstate = outerPlanState(j);
+ PlanState *innerstate = innerPlanState(j);
+
+ j->outer_pre_detoast_attrs = cal_final_pre_detoast_attrs(
+ ((Join *) j->ps.plan)->outer_reference_attrs,
+ outerstate->ps_ResultTupleDesc,
+ outerstate->plan->forbid_pre_detoast_vars);
+
+ j->inner_pre_detoast_attrs = cal_final_pre_detoast_attrs(
+ ((Join *) j->ps.plan)->inner_reference_attrs,
+ innerstate->ps_ResultTupleDesc,
+ innerstate->plan->forbid_pre_detoast_vars);
+}
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index cff5dc723e..a8646ded02 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -572,6 +572,8 @@ ExecConditionalAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc,
planstate->resultopsset = planstate->scanopsset;
planstate->resultopsfixed = planstate->scanopsfixed;
planstate->resultops = planstate->scanops;
+
+ Assert(planstate->ps_ResultTupleDesc != NULL);
}
else
{
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 1cbec4647c..19a05ed624 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -756,6 +756,8 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate, eflags);
innerDesc = ExecGetResultType(innerPlanState(hjstate));
+ SetPredetoastAttrsForJoin((JoinState *) hjstate);
+
/*
* Initialize result slot, type and projection.
*/
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index c1a8ca2464..be7cbd7f30 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -1497,6 +1497,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
(eflags | EXEC_FLAG_MARK));
innerDesc = ExecGetResultType(innerPlanState(mergestate));
+ SetPredetoastAttrsForJoin((JoinState *) mergestate);
+
/*
* For certain types of inner child nodes, it is advantageous to issue
* MARK every time we advance past an inner tuple we will never return to.
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 06fa0a9b31..2d40d19192 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -306,6 +306,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
*/
ExecInitResultTupleSlotTL(&nlstate->js.ps, &TTSOpsVirtual);
ExecAssignProjectionInfo(&nlstate->js.ps, NULL);
+ SetPredetoastAttrsForJoin((JoinState *) nlstate);
/*
* initialize child expressions
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 0c448422e2..74563c3454 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -396,30 +396,52 @@ llvm_compile_expr(ExprState *state)
case EEOP_INNER_VAR:
case EEOP_OUTER_VAR:
case EEOP_SCAN_VAR:
+ case EEOP_INNER_VAR_TOAST:
+ case EEOP_OUTER_VAR_TOAST:
+ case EEOP_SCAN_VAR_TOAST:
{
LLVMValueRef value,
isnull;
LLVMValueRef v_attnum;
LLVMValueRef v_values;
LLVMValueRef v_nulls;
+ LLVMValueRef v_slot;
- if (opcode == EEOP_INNER_VAR)
+ if (opcode == EEOP_INNER_VAR || opcode == EEOP_INNER_VAR_TOAST)
{
+ v_slot = v_innerslot;
v_values = v_innervalues;
v_nulls = v_innernulls;
}
- else if (opcode == EEOP_OUTER_VAR)
+ else if (opcode == EEOP_OUTER_VAR || opcode == EEOP_OUTER_VAR_TOAST)
{
+ v_slot = v_outerslot;
v_values = v_outervalues;
v_nulls = v_outernulls;
}
else
{
+ v_slot = v_scanslot;
v_values = v_scanvalues;
v_nulls = v_scannulls;
}
v_attnum = l_int32_const(lc, op->d.var.attnum);
+
+ if (opcode == EEOP_INNER_VAR_TOAST ||
+ opcode == EEOP_OUTER_VAR_TOAST ||
+ opcode == EEOP_SCAN_VAR_TOAST)
+ {
+ LLVMValueRef params[2];
+
+ params[0] = v_slot;
+ params[1] = l_int32_const(lc, op->d.var.attnum);
+ l_call(b,
+ llvm_pg_var_func_type("ExecSlotDetoastDatumExternal"),
+ llvm_pg_func(mod, "ExecSlotDetoastDatumExternal"),
+ params, lengthof(params), "");
+ }
+
value = l_load_gep1(b, TypeSizeT, v_values, v_attnum, "");
isnull = l_load_gep1(b, TypeStorageBool, v_nulls, v_attnum, "");
LLVMBuildStore(b, value, v_resvaluep);
diff --git a/src/backend/jit/llvm/llvmjit_types.c b/src/backend/jit/llvm/llvmjit_types.c
index 47c9daf402..1dcf0c2fd8 100644
--- a/src/backend/jit/llvm/llvmjit_types.c
+++ b/src/backend/jit/llvm/llvmjit_types.c
@@ -178,4 +178,5 @@ void *referenced_functions[] =
strlen,
varsize_any,
ExecInterpExprStillValid,
+ ExecSlotDetoastDatumExternal,
};
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 610f4a56d6..7ac933436c 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -314,7 +314,9 @@ static ModifyTable *make_modifytable(PlannerInfo *root, Plan *subplan,
List *mergeActionLists, int epqParam);
static GatherMerge *create_gather_merge_plan(PlannerInfo *root,
GatherMergePath *best_path);
-
+static void set_plan_forbid_pre_detoast_vars_recurse(Plan *plan,
+ List *small_tlist);
+static void set_plan_not_pre_detoast_vars(Plan *plan, List *small_tlist);
/*
* create_plan
@@ -346,6 +348,12 @@ create_plan(PlannerInfo *root, Path *best_path)
/* Recursively process the path tree, demanding the correct tlist result */
plan = create_plan_recurse(root, best_path, CP_EXACT_TLIST);
+ /*
+ * After the plan tree is built completed, we start to walk for which
+ * expressions should not used the shared-detoast feature.
+ */
+ set_plan_forbid_pre_detoast_vars_recurse(plan, NIL);
+
/*
* Make sure the topmost plan node's targetlist exposes the original
* column names and other decorative info. Targetlists generated within
@@ -378,6 +386,97 @@ create_plan(PlannerInfo *root, Path *best_path)
return plan;
}
+/*
+ * set_plan_forbid_pre_detoast_vars_recurse
+ * Walking the Plan tree to gather the vars which should be as small
+ * as possible, hence all of them should be used for pre detoast datum logic.
+ */
+static void
+set_plan_forbid_pre_detoast_vars_recurse(Plan *plan, List *small_tlist)
+{
+ if (plan == NULL)
+ return;
+
+ set_plan_not_pre_detoast_vars(plan, small_tlist);
+
+ /* Recurse to its subplan.. */
+ if (IsA(plan, Sort) || IsA(plan, Memoize) || IsA(plan, WindowAgg) ||
+ IsA(plan, Hash) || IsA(plan, Material) || IsA(plan, IncrementalSort))
+ {
+ List *small_tlist = get_tlist_exprs(plan->lefttree->targetlist, true);
+
+ /*
+ * For the sort-like nodes, we want the output of its subplan as small
+ * as possible, but the subplan's other expressions like Qual doesn't
+ * have this restriction since they are not output to the upper nodes.
+ * so we set the small_tlist to the subplan->targetlist.
+ */
+ set_plan_forbid_pre_detoast_vars_recurse(plan->lefttree, small_tlist);
+ }
+ else if (IsA(plan, HashJoin) && castNode(HashJoin, plan)->left_small_tlist)
+ {
+ List *small_tlist = get_tlist_exprs(plan->lefttree->targetlist, true);
+
+ /*
+ * If the left_small_tlist wants a as small as possible tlist, set it
+ * in a way like sort for the left node.
+ */
+ set_plan_forbid_pre_detoast_vars_recurse(plan->lefttree, small_tlist);
+
+ /*
+ * The righttree is a Hash node, it can be set with its own rule, so
+ * the small_tlist provided is not important, we just need to recuse
+ * to its subplan.
+ */
+ set_plan_forbid_pre_detoast_vars_recurse(plan->righttree, plan->forbid_pre_detoast_vars);
+ }
+ else
+ {
+ /*
+ * Recurse to its children, just push down the forbid_pre_detoast_vars
+ * to its children.
+ */
+ set_plan_forbid_pre_detoast_vars_recurse(plan->lefttree, plan->forbid_pre_detoast_vars);
+ set_plan_forbid_pre_detoast_vars_recurse(plan->righttree, plan->forbid_pre_detoast_vars);
+ }
+}
+
+/*
+ * set_plan_not_pre_detoast_vars
+ *
+ * Set the Plan.forbid_pre_detoast_vars according the small_tlist information.
+ *
+ * small_tlist = NIL means nothing is forbidden, or else if a Var belongs to the
+ * small_tlist, then it must not be pre-detoasted.
+ */
+static void
+set_plan_not_pre_detoast_vars(Plan *plan, List *small_tlist)
+{
+ ListCell *lc;
+ Var *var;
+
+ /*
+ * fast path, if we don't have a small_tlist, the var in targetlist is
+ * impossible member of it. and this case might be a pretty common case.
+ */
+ if (small_tlist == NIL)
+ return;
+
+ foreach(lc, plan->targetlist)
+ {
+ TargetEntry *te = lfirst_node(TargetEntry, lc);
+
+ if (!IsA(te->expr, Var))
+ continue;
+ var = castNode(Var, te->expr);
+ if (var->varattno <= 0)
+ continue;
+ if (list_member(small_tlist, var))
+ /* pass the recheck */
+ plan->forbid_pre_detoast_vars = lappend(plan->forbid_pre_detoast_vars, var);
+ }
+}
+
/*
* create_plan_recurse
* Recursive guts of create_plan().
@@ -4893,6 +4992,8 @@ create_hashjoin_plan(PlannerInfo *root,
copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
+ join_plan->left_small_tlist = (best_path->num_batches > 1);
+
return join_plan;
}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 22a1fa29f3..1847c8eada 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -27,6 +27,7 @@
#include "optimizer/tlist.h"
#include "parser/parse_relation.h"
#include "tcop/utility.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
@@ -55,11 +56,47 @@ typedef struct
tlist_vinfo vars[FLEXIBLE_ARRAY_MEMBER]; /* has num_vars entries */
} indexed_tlist;
+/*
+ * Decide which attrs are detoasted in a expressions level, this is judged
+ * at the fix_scan/join_expr stage. The recursed level is tracked when we
+ * walk to a Var, if the level is greater than 1, then it means the
+ * var needs an detoast in this expression list, there are some exceptions
+ * here, see increase_level_for_pre_detoast for details.
+ */
+typedef struct
+{
+ /* if the level is added during a certain walk. */
+ bool level_added;
+ /* the current level during the walk. */
+ int level;
+} intermediate_level_context;
+
+/*
+ * Context to hold the detoast attribute for a expression.
+ *
+ * XXX: this design was intent to avoid the pre-detoast-logic if the var
+ * only need to be detoasted *once*, but for now, the context is only
+ * maintain at the expression level. So the 'first/2+' times logic
+ * doesn't work really. Recording the times of a Var is accessed in the
+ * plan tree level is complex, so before we decide it is a must, I am not
+ * willing to do too many changes here.
+ */
+typedef struct
+{
+ /* var is accessed for the first time. */
+ Bitmapset *existing_attrs;
+ /* var is accessed for the 2+ times. */
+ Bitmapset **final_ref_attrs;
+} intermediate_var_ref_context;
+
+
typedef struct
{
PlannerInfo *root;
int rtoffset;
double num_exec;
+ intermediate_level_context level_ctx;
+ intermediate_var_ref_context scan_reference_attrs;
} fix_scan_expr_context;
typedef struct
@@ -71,6 +108,9 @@ typedef struct
int rtoffset;
NullingRelsMatch nrm_match;
double num_exec;
+ intermediate_level_context level_ctx;
+ intermediate_var_ref_context outer_reference_attrs;
+ intermediate_var_ref_context inner_reference_attrs;
} fix_join_expr_context;
typedef struct
@@ -127,8 +167,8 @@ typedef struct
(((con)->consttype == REGCLASSOID || (con)->consttype == OIDOID) && \
!(con)->constisnull)
-#define fix_scan_list(root, lst, rtoffset, num_exec) \
- ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec))
+#define fix_scan_list(root, lst, rtoffset, num_exec, pre_detoast_attrs) \
+ ((List *) fix_scan_expr(root, (Node *) (lst), rtoffset, num_exec, pre_detoast_attrs))
static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing);
static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
@@ -158,7 +198,8 @@ static Plan *set_mergeappend_references(PlannerInfo *root,
static void set_hash_references(PlannerInfo *root, Plan *plan, int rtoffset);
static Relids offset_relid_set(Relids relids, int rtoffset);
static Node *fix_scan_expr(PlannerInfo *root, Node *node,
- int rtoffset, double num_exec);
+ int rtoffset, double num_exec,
+ Bitmapset **scan_reference_attrs);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
@@ -190,7 +231,10 @@ static List *fix_join_expr(PlannerInfo *root,
Index acceptable_rel,
int rtoffset,
NullingRelsMatch nrm_match,
- double num_exec);
+ double num_exec,
+ Bitmapset **outer_reference_attrs,
+ Bitmapset **inner_reference_attrs);
+
static Node *fix_join_expr_mutator(Node *node,
fix_join_expr_context *context);
static Node *fix_upper_expr(PlannerInfo *root,
@@ -628,10 +672,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_SampleScan:
@@ -641,13 +691,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs
+ );
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tablesample = (TableSampleClause *)
fix_scan_expr(root, (Node *) splan->tablesample,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_IndexScan:
@@ -657,28 +714,40 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
+
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
splan->indexqual =
fix_scan_list(root, splan->indexqual,
- rtoffset, 1);
+ rtoffset, 1, &splan->scan.reference_attrs);
splan->indexqualorig =
fix_scan_list(root, splan->indexqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->indexorderby =
fix_scan_list(root, splan->indexorderby,
- rtoffset, 1);
+ rtoffset, 1, &splan->scan.reference_attrs);
splan->indexorderbyorig =
fix_scan_list(root, splan->indexorderbyorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan), &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_IndexOnlyScan:
{
IndexOnlyScan *splan = (IndexOnlyScan *) plan;
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
+
return set_indexonlyscan_references(root, splan, rtoffset);
}
break;
@@ -691,10 +760,15 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->scan.plan.targetlist == NIL);
Assert(splan->scan.plan.qual == NIL);
splan->indexqual =
- fix_scan_list(root, splan->indexqual, rtoffset, 1);
+ fix_scan_list(root, splan->indexqual, rtoffset, 1,
+ &splan->scan.reference_attrs);
splan->indexqualorig =
fix_scan_list(root, splan->indexqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_BitmapHeapScan:
@@ -704,13 +778,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->bitmapqualorig =
fix_scan_list(root, splan->bitmapqualorig,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_TidScan:
@@ -720,13 +801,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tidquals =
fix_scan_list(root, splan->tidquals,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_TidRangeScan:
@@ -736,13 +824,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->tidrangequals =
fix_scan_list(root, splan->tidrangequals,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_SubqueryScan:
@@ -757,12 +852,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->functions =
- fix_scan_list(root, splan->functions, rtoffset, 1);
+ fix_scan_list(root, splan->functions, rtoffset, 1,
+ &splan->scan.reference_attrs);
+
}
break;
case T_TableFuncScan:
@@ -772,13 +871,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+
splan->tablefunc = (TableFunc *)
fix_scan_expr(root, (Node *) splan->tablefunc,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
}
break;
case T_ValuesScan:
@@ -788,13 +891,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
splan->values_lists =
fix_scan_list(root, splan->values_lists,
- rtoffset, 1);
+ rtoffset, 1,
+ &splan->scan.reference_attrs);
}
break;
case T_CteScan:
@@ -804,10 +910,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
+ splan->scan.plan.forbid_pre_detoast_vars =
+ fix_scan_list(root, splan->scan.plan.forbid_pre_detoast_vars,
+ rtoffset, NUM_EXEC_TLIST(plan),
+ NULL);
}
break;
case T_NamedTuplestoreScan:
@@ -817,10 +929,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
}
break;
case T_WorkTableScan:
@@ -830,10 +944,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(root, splan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan),
+ &splan->scan.reference_attrs);
splan->scan.plan.qual =
fix_scan_list(root, splan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan),
+ &splan->scan.reference_attrs);
}
break;
case T_ForeignScan:
@@ -873,7 +989,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
mplan->param_exprs = fix_scan_list(root, mplan->param_exprs,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan),
+ NULL);
break;
}
@@ -933,9 +1050,9 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
Assert(splan->plan.qual == NIL);
splan->limitOffset =
- fix_scan_expr(root, splan->limitOffset, rtoffset, 1);
+ fix_scan_expr(root, splan->limitOffset, rtoffset, 1, NULL);
splan->limitCount =
- fix_scan_expr(root, splan->limitCount, rtoffset, 1);
+ fix_scan_expr(root, splan->limitCount, rtoffset, 1, NULL);
}
break;
case T_Agg:
@@ -988,17 +1105,17 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
* variable refs, so fix_scan_expr works for them.
*/
wplan->startOffset =
- fix_scan_expr(root, wplan->startOffset, rtoffset, 1);
+ fix_scan_expr(root, wplan->startOffset, rtoffset, 1, NULL);
wplan->endOffset =
- fix_scan_expr(root, wplan->endOffset, rtoffset, 1);
+ fix_scan_expr(root, wplan->endOffset, rtoffset, 1, NULL);
wplan->runCondition = fix_scan_list(root,
wplan->runCondition,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan), NULL);
wplan->runConditionOrig = fix_scan_list(root,
wplan->runConditionOrig,
rtoffset,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan), NULL);
}
break;
case T_Result:
@@ -1038,14 +1155,14 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->plan.targetlist =
fix_scan_list(root, splan->plan.targetlist,
- rtoffset, NUM_EXEC_TLIST(plan));
+ rtoffset, NUM_EXEC_TLIST(plan), NULL);
splan->plan.qual =
fix_scan_list(root, splan->plan.qual,
- rtoffset, NUM_EXEC_QUAL(plan));
+ rtoffset, NUM_EXEC_QUAL(plan), NULL);
}
/* resconstantqual can't contain any subplan variable refs */
splan->resconstantqual =
- fix_scan_expr(root, splan->resconstantqual, rtoffset, 1);
+ fix_scan_expr(root, splan->resconstantqual, rtoffset, 1, NULL);
}
break;
case T_ProjectSet:
@@ -1061,7 +1178,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
splan->withCheckOptionLists =
fix_scan_list(root, splan->withCheckOptionLists,
- rtoffset, 1);
+ rtoffset, 1, NULL);
if (splan->returningLists)
{
@@ -1118,18 +1235,20 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
fix_join_expr(root, splan->onConflictSet,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan),
+ NULL, NULL);
splan->onConflictWhere = (Node *)
fix_join_expr(root, (List *) splan->onConflictWhere,
NULL, itlist,
linitial_int(splan->resultRelations),
- rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan));
+ rtoffset, NRM_EQUAL, NUM_EXEC_QUAL(plan),
+ NULL, NULL);
pfree(itlist);
splan->exclRelTlist =
- fix_scan_list(root, splan->exclRelTlist, rtoffset, 1);
+ fix_scan_list(root, splan->exclRelTlist, rtoffset, 1, NULL);
}
/*
@@ -1182,7 +1301,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
resultrel,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_TLIST(plan));
+ NUM_EXEC_TLIST(plan),
+ NULL, NULL);
/* Fix quals too. */
action->qual = (Node *) fix_join_expr(root,
@@ -1191,7 +1311,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
resultrel,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL(plan));
+ NUM_EXEC_QUAL(plan),
+ NULL, NULL);
}
}
}
@@ -1356,13 +1477,16 @@ set_indexonlyscan_references(PlannerInfo *root,
NUM_EXEC_QUAL((Plan *) plan));
/* indexqual is already transformed to reference index columns */
plan->indexqual = fix_scan_list(root, plan->indexqual,
- rtoffset, 1);
+ rtoffset, 1,
+ &plan->scan.reference_attrs);
/* indexorderby is already transformed to reference index columns */
plan->indexorderby = fix_scan_list(root, plan->indexorderby,
- rtoffset, 1);
+ rtoffset, 1,
+ &plan->scan.reference_attrs);
/* indextlist must NOT be transformed to reference index columns */
plan->indextlist = fix_scan_list(root, plan->indextlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) plan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan),
+ &plan->scan.reference_attrs);
pfree(index_itlist);
@@ -1409,10 +1533,10 @@ set_subqueryscan_references(PlannerInfo *root,
plan->scan.scanrelid += rtoffset;
plan->scan.plan.targetlist =
fix_scan_list(root, plan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) plan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) plan), NULL);
plan->scan.plan.qual =
fix_scan_list(root, plan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) plan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) plan), NULL);
result = (Plan *) plan;
}
@@ -1612,7 +1736,7 @@ set_foreignscan_references(PlannerInfo *root,
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
fscan->fdw_scan_tlist =
fix_scan_list(root, fscan->fdw_scan_tlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan), NULL);
}
else
{
@@ -1622,16 +1746,16 @@ set_foreignscan_references(PlannerInfo *root,
*/
fscan->scan.plan.targetlist =
fix_scan_list(root, fscan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) fscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) fscan), NULL);
fscan->scan.plan.qual =
fix_scan_list(root, fscan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
fscan->fdw_exprs =
fix_scan_list(root, fscan->fdw_exprs,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
fscan->fdw_recheck_quals =
fix_scan_list(root, fscan->fdw_recheck_quals,
- rtoffset, NUM_EXEC_QUAL((Plan *) fscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) fscan), NULL);
}
fscan->fs_relids = offset_relid_set(fscan->fs_relids, rtoffset);
@@ -1690,20 +1814,20 @@ set_customscan_references(PlannerInfo *root,
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
cscan->custom_scan_tlist =
fix_scan_list(root, cscan->custom_scan_tlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan), NULL);
}
else
{
/* Adjust tlist, qual, custom_exprs in the standard way */
cscan->scan.plan.targetlist =
fix_scan_list(root, cscan->scan.plan.targetlist,
- rtoffset, NUM_EXEC_TLIST((Plan *) cscan));
+ rtoffset, NUM_EXEC_TLIST((Plan *) cscan), NULL);
cscan->scan.plan.qual =
fix_scan_list(root, cscan->scan.plan.qual,
- rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan), NULL);
cscan->custom_exprs =
fix_scan_list(root, cscan->custom_exprs,
- rtoffset, NUM_EXEC_QUAL((Plan *) cscan));
+ rtoffset, NUM_EXEC_QUAL((Plan *) cscan), NULL);
}
/* Adjust child plan-nodes recursively, if needed */
@@ -2111,6 +2235,95 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
return (Node *) bestplan;
}
+
+static inline void
+setup_intermediate_level_ctx(intermediate_level_context *ctx)
+{
+ ctx->level = 0;
+ ctx->level_added = false;
+}
+
+static inline void
+setup_intermediate_var_ref_ctx(intermediate_var_ref_context *ctx, Bitmapset **final_ref_attrs)
+{
+ ctx->existing_attrs = NULL;
+ ctx->final_ref_attrs = final_ref_attrs;
+}
+
+/*
+ * increase_level_for_pre_detoast
+ * Check if the given Expr could detoast a Var directly, if yes,
+ * increase the level and return true. otherwise return false;
+ */
+static inline void
+increase_level_for_pre_detoast(Node *node, intermediate_level_context *ctx)
+{
+ /* The following nodes is impossible to detoast a Var directly. */
+ if (IsA(node, List) || IsA(node, TargetEntry) || IsA(node, NullTest))
+ {
+ ctx->level_added = false;
+ }
+ else if (IsA(node, FuncExpr) && castNode(FuncExpr, node)->funcid == F_PG_COLUMN_COMPRESSION)
+ {
+ /* let's not detoast first so that pg_column_compression works. */
+ ctx->level_added = false;
+ }
+ else
+ {
+ ctx->level_added = true;
+ ctx->level += 1;
+ }
+}
+
+static inline void
+decreased_level_for_pre_detoast(intermediate_level_context *ctx)
+{
+ if (ctx->level_added)
+ ctx->level -= 1;
+
+ ctx->level_added = false;
+}
+
+/*
+ * add_pre_detoast_vars
+ * add the var's information into pre_detoast_attrs when the check is pass.
+ */
+static inline void
+add_pre_detoast_vars(intermediate_level_context *level_ctx,
+ intermediate_var_ref_context *ctx,
+ Var *var)
+{
+ int attno;
+
+ if (level_ctx->level <= 1 || ctx->final_ref_attrs == NULL || var->varattno <= 0)
+ return;
+
+ attno = var->varattno - 1;
+ if (bms_is_member(attno, ctx->existing_attrs))
+ {
+ /* not the first time to access it, add it to final result. */
+ *ctx->final_ref_attrs = bms_add_member(*ctx->final_ref_attrs, attno);
+ }
+ else
+ {
+ /* first time. */
+ ctx->existing_attrs = bms_add_member(ctx->existing_attrs, attno);
+
+ /*
+ * XXX:
+ *
+ * The above strategy doesn't help to detect if a Var is detoast
+ * twice. Reasons are: 1. the context is not maintain in Plan node
+ * level. so if it is detoast at targetlist and qual, we can't detect
+ * it. 2. even we can make it at plan node, it still doesn't help for
+ * the among-nodes case.
+ *
+ * So for now, I just disable it.
+ */
+ *ctx->final_ref_attrs = bms_add_member(*ctx->final_ref_attrs, attno);
+ }
+}
+
/*
* fix_scan_expr
* Do set_plan_references processing on a scan-level expression
@@ -2125,18 +2338,23 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
* 'node': the expression to be modified
* 'rtoffset': how much to increment varnos by
* 'num_exec': estimated number of executions of expression
+ * 'scan_reference_attrs': gather which vars are potential to run the detoast
+ * on this expr, NULL means the caller doesn't have interests on this.
*
* The expression tree is either copied-and-modified, or modified in-place
* if that seems safe.
*/
static Node *
-fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
+fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset,
+ double num_exec, Bitmapset **scan_reference_attrs)
{
fix_scan_expr_context context;
context.root = root;
context.rtoffset = rtoffset;
context.num_exec = num_exec;
+ setup_intermediate_level_ctx(&context.level_ctx);
+ setup_intermediate_var_ref_ctx(&context.scan_reference_attrs, scan_reference_attrs);
if (rtoffset != 0 ||
root->multiexpr_params != NIL ||
@@ -2167,8 +2385,13 @@ fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset, double num_exec)
static Node *
fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
{
+ Node *n;
+
if (node == NULL)
return NULL;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
if (IsA(node, Var))
{
Var *var = copyVar((Var *) node);
@@ -2186,10 +2409,16 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
var->varno += context->rtoffset;
if (var->varnosyn > 0)
var->varnosyn += context->rtoffset;
+
+ add_pre_detoast_vars(&context->level_ctx, &context->scan_reference_attrs, var);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) var;
}
if (IsA(node, Param))
+ {
+ decreased_level_for_pre_detoast(&context->level_ctx);
return fix_param_node(context->root, (Param *) node);
+ }
if (IsA(node, Aggref))
{
Aggref *aggref = (Aggref *) node;
@@ -2199,8 +2428,10 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
aggparam = find_minmax_agg_replacement_param(context->root, aggref);
if (aggparam != NULL)
{
+ decreased_level_for_pre_detoast(&context->level_ctx);
/* Make a copy of the Param for paranoia's sake */
return (Node *) copyObject(aggparam);
+
}
/* If no match, just fall through to process it normally */
}
@@ -2210,6 +2441,7 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
Assert(!IS_SPECIAL_VARNO(cexpr->cvarno));
cexpr->cvarno += context->rtoffset;
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) cexpr;
}
if (IsA(node, PlaceHolderVar))
@@ -2218,29 +2450,52 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context)
PlaceHolderVar *phv = (PlaceHolderVar *) node;
/* XXX can we assert something about phnullingrels? */
- return fix_scan_expr_mutator((Node *) phv->phexpr, context);
+ Node *n2 = fix_scan_expr_mutator((Node *) phv->phexpr, context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n2;
}
if (IsA(node, AlternativeSubPlan))
- return fix_scan_expr_mutator(fix_alternative_subplan(context->root,
- (AlternativeSubPlan *) node,
- context->num_exec),
- context);
+ {
+ Node *n2 = fix_scan_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n2;
+ }
fix_expr_common(context->root, node);
- return expression_tree_mutator(node, fix_scan_expr_mutator,
- (void *) context);
+ n = expression_tree_mutator(node, fix_scan_expr_mutator, (void *) context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return n;
}
static bool
fix_scan_expr_walker(Node *node, fix_scan_expr_context *context)
{
+ bool ret;
+
if (node == NULL)
return false;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
+ if (IsA(node, Var))
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->scan_reference_attrs,
+ castNode(Var, node));
+ }
Assert(!(IsA(node, Var) && ((Var *) node)->varno == ROWID_VAR));
Assert(!IsA(node, PlaceHolderVar));
Assert(!IsA(node, AlternativeSubPlan));
fix_expr_common(context->root, node);
- return expression_tree_walker(node, fix_scan_expr_walker,
- (void *) context);
+ ret = expression_tree_walker(node, fix_scan_expr_walker,
+ (void *) context);
+
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret;
}
/*
@@ -2276,7 +2531,10 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs
+ );
/* Now do join-type-specific stuff */
if (IsA(join, NestLoop))
@@ -2323,7 +2581,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
}
else if (IsA(join, HashJoin))
{
@@ -2336,7 +2596,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_QUAL((Plan *) join));
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
/*
* HashJoin's hashkeys are used to look for matching tuples from its
@@ -2368,7 +2630,9 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
(join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
- NUM_EXEC_TLIST((Plan *) join));
+ NUM_EXEC_TLIST((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
join->plan.qual = fix_join_expr(root,
join->plan.qual,
outer_itlist,
@@ -2376,8 +2640,20 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset)
(Index) 0,
rtoffset,
(join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
- NUM_EXEC_QUAL((Plan *) join));
-
+ NUM_EXEC_QUAL((Plan *) join),
+ &join->outer_reference_attrs,
+ &join->inner_reference_attrs);
+
+ join->plan.forbid_pre_detoast_vars = fix_join_expr(root,
+ join->plan.forbid_pre_detoast_vars,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset,
+ (join->jointype == JOIN_INNER ? NRM_EQUAL : NRM_SUPERSET),
+ NUM_EXEC_TLIST((Plan *) join),
+ NULL,
+ NULL);
pfree(outer_itlist);
pfree(inner_itlist);
}
@@ -3010,9 +3286,12 @@ fix_join_expr(PlannerInfo *root,
Index acceptable_rel,
int rtoffset,
NullingRelsMatch nrm_match,
- double num_exec)
+ double num_exec,
+ Bitmapset **outer_reference_attrs,
+ Bitmapset **inner_reference_attrs)
{
fix_join_expr_context context;
+ List *ret;
context.root = root;
context.outer_itlist = outer_itlist;
@@ -3021,16 +3300,30 @@ fix_join_expr(PlannerInfo *root,
context.rtoffset = rtoffset;
context.nrm_match = nrm_match;
context.num_exec = num_exec;
- return (List *) fix_join_expr_mutator((Node *) clauses, &context);
+
+ setup_intermediate_level_ctx(&context.level_ctx);
+ setup_intermediate_var_ref_ctx(&context.outer_reference_attrs, outer_reference_attrs);
+ setup_intermediate_var_ref_ctx(&context.inner_reference_attrs, inner_reference_attrs);
+
+ ret = (List *) fix_join_expr_mutator((Node *) clauses, &context);
+
+ bms_free(context.outer_reference_attrs.existing_attrs);
+ bms_free(context.inner_reference_attrs.existing_attrs);
+
+ return ret;
}
static Node *
fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
{
Var *newvar;
+ Node *ret_node;
if (node == NULL)
return NULL;
+
+ increase_level_for_pre_detoast(node, &context->level_ctx);
+
if (IsA(node, Var))
{
Var *var = (Var *) node;
@@ -3044,7 +3337,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->rtoffset,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* then in the inner. */
@@ -3056,7 +3355,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->rtoffset,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* If it's for acceptable_rel, adjust and return it */
@@ -3066,6 +3371,9 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
var->varno += context->rtoffset;
if (var->varnosyn > 0)
var->varnosyn += context->rtoffset;
+ /* XXX acceptable_rel? we can ignore it for safety. */
+ decreased_level_for_pre_detoast(&context->level_ctx);
+
return (Node *) var;
}
@@ -3084,22 +3392,38 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
OUTER_VAR,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
if (context->inner_itlist && context->inner_itlist->has_ph_vars)
{
+
newvar = search_indexed_tlist_for_phv(phv,
context->inner_itlist,
INNER_VAR,
context->nrm_match);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* If not supplied by input plans, evaluate the contained expr */
/* XXX can we assert something about phnullingrels? */
- return fix_join_expr_mutator((Node *) phv->phexpr, context);
+ ret_node = fix_join_expr_mutator((Node *) phv->phexpr, context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
}
+
/* Try matching more complex expressions too, if tlists have any */
if (context->outer_itlist && context->outer_itlist->has_non_vars)
{
@@ -3107,7 +3431,13 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->outer_itlist,
OUTER_VAR);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->outer_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
if (context->inner_itlist && context->inner_itlist->has_non_vars)
{
@@ -3115,20 +3445,36 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
context->inner_itlist,
INNER_VAR);
if (newvar)
+ {
+ add_pre_detoast_vars(&context->level_ctx,
+ &context->inner_reference_attrs,
+ newvar);
+ decreased_level_for_pre_detoast(&context->level_ctx);
return (Node *) newvar;
+ }
}
/* Special cases (apply only AFTER failing to match to lower tlist) */
if (IsA(node, Param))
- return fix_param_node(context->root, (Param *) node);
+ {
+ ret_node = fix_param_node(context->root, (Param *) node);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
+ }
if (IsA(node, AlternativeSubPlan))
- return fix_join_expr_mutator(fix_alternative_subplan(context->root,
- (AlternativeSubPlan *) node,
- context->num_exec),
- context);
+ {
+ ret_node = fix_join_expr_mutator(fix_alternative_subplan(context->root,
+ (AlternativeSubPlan *) node,
+ context->num_exec),
+ context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
+ }
fix_expr_common(context->root, node);
- return expression_tree_mutator(node,
- fix_join_expr_mutator,
- (void *) context);
+ ret_node = expression_tree_mutator(node,
+ fix_join_expr_mutator,
+ (void *) context);
+ decreased_level_for_pre_detoast(&context->level_ctx);
+ return ret_node;
}
/*
@@ -3163,7 +3509,8 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
* varno = newvarno, varattno = resno of corresponding targetlist element.
* The original tree is not modified.
*/
-static Node *
+static Node * /* XXX: shall I care about this for shared
+ * detoast optimization? */
fix_upper_expr(PlannerInfo *root,
Node *node,
indexed_tlist *subplan_itlist,
@@ -3318,7 +3665,10 @@ set_returning_clause_references(PlannerInfo *root,
resultRelation,
rtoffset,
NRM_EQUAL,
- NUM_EXEC_TLIST(topplan));
+ NUM_EXEC_TLIST(topplan),
+ NULL,
+ NULL
+ );
pfree(itlist);
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index a28ddcdd77..9304786bb2 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -78,6 +78,17 @@ typedef enum ExprEvalOp
EEOP_OUTER_VAR,
EEOP_SCAN_VAR,
+ /*
+ * compute non-system Var value with shared-detoast-datum logic, use some
+ * dedicated steps rather than add extra logic to existing steps is for
+ * performance aspect, within this way, we just decide if the extra logic
+ * is needed at ExecInitExpr stage once rather than every time of
+ * ExecInterpExpr.
+ */
+ EEOP_INNER_VAR_TOAST,
+ EEOP_OUTER_VAR_TOAST,
+ EEOP_SCAN_VAR_TOAST,
+
/* compute system Var value */
EEOP_INNER_SYSVAR,
EEOP_OUTER_SYSVAR,
@@ -830,5 +841,6 @@ extern void ExecEvalAggOrderedTransDatum(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
+extern void ExecSlotDetoastDatumExternal(TupleTableSlot *slot, int attnum);
#endif /* EXEC_EXPR_H */
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 6133dbcd0a..2e04bb028e 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -18,6 +18,7 @@
#include "access/htup_details.h"
#include "access/sysattr.h"
#include "access/tupdesc.h"
+#include "nodes/bitmapset.h"
#include "storage/buf.h"
/*----------
@@ -128,6 +129,18 @@ typedef struct TupleTableSlot
MemoryContext tts_mcxt; /* slot itself is in this context */
ItemPointerData tts_tid; /* stored tuple's tid */
Oid tts_tableOid; /* table oid of tuple */
+
+ /*
+ * The attributes whose values are the detoasted version in tts_values[*],
+ * if so these memory needs some extra clearup. These memory can't be put
+ * into ecxt_per_tuple_memory since many of them needs a longer life span,
+ * for example the Datum in outer join. These memory is put into
+ * TupleTableSlot.tts_mcxt and be clear whenever the tts_values[*] is
+ * invalidated.
+ *
+ * These values are populated by EEOP_{INNER/OUTER/SCAN}_VAR_TOAST steps.
+ */
+ Bitset *pre_detoasted_attrs;
} TupleTableSlot;
/* routines for a TupleTableSlot implementation */
@@ -426,12 +439,36 @@ slot_getsysattr(TupleTableSlot *slot, int attnum, bool *isnull)
return slot->tts_ops->getsysattr(slot, attnum, isnull);
}
+/*
+ * ExecFreePreDetoastDatum - free the memory which is allocated in pre-detoast-datum.
+ */
+static inline void
+ExecFreePreDetoastDatum(TupleTableSlot *slot)
+{
+ int attnum;
+
+ attnum = -1;
+ while ((attnum = bitset_next_member(slot->pre_detoasted_attrs, attnum)) >= 0)
+ {
+ pfree((void *) slot->tts_values[attnum]);
+ }
+
+ /*
+ * unset the bits but keep the memory for later use, this is importance
+ * for at the performance aspect.
+ */
+ bitset_clear(slot->pre_detoasted_attrs);
+}
+
+
/*
* ExecClearTuple - clear the slot's contents
*/
static inline TupleTableSlot *
ExecClearTuple(TupleTableSlot *slot)
{
+ ExecFreePreDetoastDatum(slot);
+
slot->tts_ops->clear(slot);
return slot;
@@ -450,6 +487,10 @@ ExecClearTuple(TupleTableSlot *slot)
static inline void
ExecMaterializeSlot(TupleTableSlot *slot)
{
+ /*
+ * XXX: pre_detoasted_attrs doesn't dependent on any external storage, so
+ * nothing should be done here.
+ */
slot->tts_ops->materialize(slot);
}
@@ -494,6 +535,25 @@ ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
dstslot->tts_ops->copyslot(dstslot, srcslot);
+ if (dstslot->tts_nvalid > 0 && srcslot->tts_nvalid > 0)
+ {
+ int attnum = -1;
+ MemoryContext old = MemoryContextSwitchTo(dstslot->tts_mcxt);
+
+ dstslot->pre_detoasted_attrs = bitset_copy(srcslot->pre_detoasted_attrs);
+
+ while ((attnum = bitset_next_member(dstslot->pre_detoasted_attrs, attnum)) >= 0)
+ {
+ struct varlena *datum = (struct varlena *) srcslot->tts_values[attnum];
+ Size len;
+
+ Assert(!VARATT_IS_EXTENDED(datum));
+ len = VARSIZE(datum);
+ dstslot->tts_values[attnum] = (Datum) palloc(len);
+ memcpy((void *) dstslot->tts_values[attnum], datum, len);
+ }
+ MemoryContextSwitchTo(old);
+ }
return dstslot;
}
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 444a5f0fd5..30fdb37d1c 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1481,6 +1481,12 @@ typedef struct ScanState
Relation ss_currentRelation;
struct TableScanDescData *ss_currentScanDesc;
TupleTableSlot *ss_ScanTupleSlot;
+
+ /*
+ * The final attributes which should apply the pre-detoast-attrs logic on
+ * the Scan nodes.
+ */
+ Bitmapset *scan_pre_detoast_attrs;
} ScanState;
/* ----------------
@@ -2010,6 +2016,13 @@ typedef struct JoinState
bool single_match; /* True if we should skip to next outer tuple
* after finding one inner match */
ExprState *joinqual; /* JOIN quals (in addition to ps.qual) */
+
+ /*
+ * The final attributes which should apply the pre-detoast-attrs logic on
+ * the join nodes.
+ */
+ Bitmapset *outer_pre_detoast_attrs;
+ Bitmapset *inner_pre_detoast_attrs;
} JoinState;
/* ----------------
@@ -2771,4 +2784,5 @@ typedef struct LimitState
TupleTableSlot *last_slot; /* slot for evaluation of ties */
} LimitState;
+extern void SetPredetoastAttrsForJoin(JoinState *joinstate);
#endif /* EXECNODES_H */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index b4ef6bc44c..ea5033aaa0 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -169,6 +169,13 @@ typedef struct Plan
*/
Bitmapset *extParam;
Bitmapset *allParam;
+
+ /*
+ * A list of Vars which should not apply the shared-detoast-datum logic
+ * since the upper nodes like Sort/Hash wants them as small as possible.
+ * It's a subset of targetlist in each Plan node.
+ */
+ List *forbid_pre_detoast_vars;
} Plan;
/* ----------------
@@ -385,6 +392,16 @@ typedef struct Scan
Plan plan;
Index scanrelid; /* relid is index into the range table */
+
+ /*
+ * Records of var's varattno - 1 where the Var is accessed indirectly by
+ * any expression, like a > 3. However a IS [NOT] NULL is not included
+ * since it doesn't access the tts_values[*] at all.
+ *
+ * This is a essential information to figure out which attrs should use
+ * the pre-detoast-attrs logic.
+ */
+ Bitmapset *reference_attrs;
} Scan;
/* ----------------
@@ -789,6 +806,17 @@ typedef struct Join
JoinType jointype;
bool inner_unique;
List *joinqual; /* JOIN quals (in addition to plan.qual) */
+
+ /*
+ * Records of var's varattno - 1 where the Var is accessed indirectly by
+ * any expression, like a > 3. However a IS [NOT] NULL is not included
+ * since it doesn't access the tts_values[*] at all.
+ *
+ * This is a essential information to figure out which attrs should use
+ * the pre-detoast-attrs logic.
+ */
+ Bitmapset *outer_reference_attrs;
+ Bitmapset *inner_reference_attrs;
} Join;
/* ----------------
@@ -869,6 +897,11 @@ typedef struct HashJoin
* perform lookups in the hashtable over the inner plan.
*/
List *hashkeys;
+
+ /*
+ * Whether the left plan tree should use a SMALL_TLIST.
+ */
+ bool left_small_tlist;
} HashJoin;
/* ----------------
@@ -1588,4 +1621,24 @@ typedef enum MonotonicFunction
MONOTONICFUNC_BOTH = MONOTONICFUNC_INCREASING | MONOTONICFUNC_DECREASING,
} MonotonicFunction;
+static inline bool
+is_join_plan(Plan *plan)
+{
+ return (plan != NULL) && (IsA(plan, NestLoop) || IsA(plan, HashJoin) || IsA(plan, MergeJoin));
+}
+
+static inline bool
+is_scan_plan(Plan *plan)
+{
+ return (plan != NULL) &&
+ (IsA(plan, SeqScan) ||
+ IsA(plan, SampleScan) ||
+ IsA(plan, IndexScan) ||
+ IsA(plan, IndexOnlyScan) ||
+ IsA(plan, BitmapIndexScan) ||
+ IsA(plan, BitmapHeapScan) ||
+ IsA(plan, TidScan) ||
+ IsA(plan, SubqueryScan));
+}
+
#endif /* PLANNODES_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index dcba329ee4..308f19d61a 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -4047,6 +4047,8 @@ cb_cleanup_dir
cb_options
cb_tablespace
cb_tablespace_mapping
+intermediate_var_ref_context
+intermediate_level_context
manifest_data
manifest_writer
rfile
--
2.34.1
^ permalink raw reply [nested|flat] 11+ messages in thread
* Re: Shared detoast Datum proposal
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-06 15:12 ` Re: Shared detoast Datum proposal vignesh C <[email protected]>
2024-01-07 01:10 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-08 09:52 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-22 06:41 ` Re: Shared detoast Datum proposal Peter Smith <[email protected]>
2024-01-23 05:44 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-23 08:59 ` Re: Shared detoast Datum proposal Michael Zhilin <[email protected]>
2024-01-23 19:18 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-02-20 06:20 ` Re: Shared detoast Datum proposal Andy Fan <[email protected]>
@ 2024-02-20 18:15 ` Tomas Vondra <[email protected]>
0 siblings, 0 replies; 11+ messages in thread
From: Tomas Vondra @ 2024-02-20 18:15 UTC (permalink / raw)
To: Andy Fan <[email protected]>; +Cc: Michael Zhilin <[email protected]>; Peter Smith <[email protected]>; vignesh C <[email protected]>; Matthias van de Meent <[email protected]>; Nikita Malakhov <[email protected]>; [email protected]
Hi,
I took a quick look on this thread/patch today, so let me share a couple
initial thoughts. I may not have a particularly coherent/consistent
opinion on the patch or what would be a better way to do this yet, but
perhaps it'll start a discussion ...
The goal of the patch (as I understand it) is essentially to cache
detoasted values, so that the value does not need to be detoasted
repeatedly in different parts of the plan. I think that's perfectly
sensible and worthwhile goal - detoasting is not cheap, and complex
plans may easily spend a lot of time on it.
That being said, the approach seems somewhat invasive, and touching
parts I wouldn't have expected to need a change to implement this. For
example, I certainly would not have guessed the patch to need changes in
createplan.c or setrefs.c.
Perhaps it really needs to do these things, but neither the thread nor
the comments are very enlightening as for why it's needed :-( In many
cases I can guess, but I'm not sure my guess is correct. And comments in
code generally describe what's happening locally / next line, not the
bigger picture & why it's happening.
IIUC we walk the plan to decide which Vars should be detoasted (and
cached) once, and which cases should not do that because it'd inflate
the amount of data we need to keep in a Sort, Hash etc. Not sure if
there's a better way to do this - it depends on what happens in the
upper parts of the plan, so we can't decide while building the paths.
But maybe we could decide this while transforming the paths into a plan?
(I realize the JIT thread nearby needs to do something like that in
create_plan, and in that one I suggested maybe walking the plan would be
a better approach, so I may be contradicting myself a little bit.).
In any case, set_plan_forbid_pre_detoast_vars_recurse should probably
explain the overall strategy / reasoning in a bit more detail. Maybe
it's somewhere in this thread, but that's not great for reviewers.
Similar for the setrefs.c changes. It seems a bit suspicious to piggy
back the new code into fix_scan_expr/fix_scan_list and similar code.
Those functions have a pretty clearly defined purpose, not sure we want
to also extend them to also deal with this new thing. (FWIW I'd 100%%
did it this way if I hacked on a PoC of this, to make it work. But I'm
not sure it's the right solution.)
I don't know what to thing about the Bitset - maybe it's necessary, but
how would I know? I don't have any way to measure the benefits, because
the 0002 patch uses it right away. I think it should be done the other
way around, i.e. the patch should introduce the main feature first
(using the traditional Bitmapset), and then add Bitset on top of that.
That way we could easily measure the impact and see if it's useful.
On the whole, my biggest concern is memory usage & leaks. It's not
difficult to already have problems with large detoasted values, and if
we start keeping more of them, that may get worse. Or at least that's my
intuition - it can't really get better by keeping the values longer, right?
The other thing is the risk of leaks (in the sense of keeping detoasted
values longer than expected). I see the values are allocated in
tts_mcxt, and maybe that's the right solution - not sure.
FWIW while looking at the patch, I couldn't help but to think about
expanded datums. There's similarity in what these two features do - keep
detoasted values for a while, so that we don't need to do the expensive
processing if we access them repeatedly. Of course, expanded datums are
not meant to be long-lived, while "shared detoasted values" are meant to
exist (potentially) for the query duration. But maybe there's something
we could learn from expanded datums? For example how the varlena pointer
is leveraged to point to the expanded object.
For example, what if we add a "TOAST cache" as a query-level hash table,
and modify the detoasting to first check the hash table (with the TOAST
pointer as a key)? It'd be fairly trivial to enforce a memory limit on
the hash table, evict values from it, etc. And it wouldn't require any
of the createplan/setrefs changes, I think ...
regards
--
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
^ permalink raw reply [nested|flat] 11+ messages in thread
* [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10
@ 2026-04-17 18:20 Nathan Bossart <[email protected]>
0 siblings, 0 replies; 11+ messages in thread
From: Nathan Bossart @ 2026-04-17 18:20 UTC (permalink / raw)
---
doc/src/sgml/ref/pgupgrade.sgml | 2 +-
src/bin/pg_upgrade/check.c | 157 +----------------------
src/bin/pg_upgrade/controldata.c | 12 +-
src/bin/pg_upgrade/exec.c | 23 ----
src/bin/pg_upgrade/file.c | 164 -------------------------
src/bin/pg_upgrade/multixact_rewrite.c | 11 +-
src/bin/pg_upgrade/pg_upgrade.c | 31 +----
src/bin/pg_upgrade/pg_upgrade.h | 29 -----
src/bin/pg_upgrade/relfilenumber.c | 37 +-----
src/bin/pg_upgrade/version.c | 127 -------------------
10 files changed, 16 insertions(+), 577 deletions(-)
diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml
index 38ca09b423c..c4ed75211db 100644
--- a/doc/src/sgml/ref/pgupgrade.sgml
+++ b/doc/src/sgml/ref/pgupgrade.sgml
@@ -67,7 +67,7 @@ PostgreSQL documentation
</para>
<para>
- <application>pg_upgrade</application> supports upgrades from 9.2.X and later to the current
+ <application>pg_upgrade</application> supports upgrades from 10.X and later to the current
major release of <productname>PostgreSQL</productname>, including snapshot and beta releases.
</para>
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index f5c93e611d2..f1e35a6af47 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -28,7 +28,6 @@ static void check_for_incompatible_polymorphics(ClusterInfo *cluster);
static void check_for_tables_with_oids(ClusterInfo *cluster);
static void check_for_not_null_inheritance(ClusterInfo *cluster);
static void check_for_gist_inet_ops(ClusterInfo *cluster);
-static void check_for_pg_role_prefix(ClusterInfo *cluster);
static void check_for_new_tablespace_dir(void);
static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
static void check_for_unicode_update(ClusterInfo *cluster);
@@ -128,26 +127,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
.threshold_version = ALL_VERSIONS
},
- /*
- * 9.3 -> 9.4 Fully implement the 'line' data type in 9.4, which
- * previously returned "not enabled" by default and was only functionally
- * enabled with a compile-time switch; as of 9.4 "line" has a different
- * on-disk representation format.
- */
- {
- .status = gettext_noop("Checking for incompatible \"line\" data type"),
- .report_filename = "tables_using_line.txt",
- .base_query =
- "SELECT 'pg_catalog.line'::pg_catalog.regtype AS oid",
- .report_text =
- gettext_noop("Your installation contains the \"line\" data type in user tables.\n"
- "This data type changed its internal and input/output format\n"
- "between your old and new versions so this\n"
- "cluster cannot currently be upgraded. You can\n"
- "drop the problem columns and restart the upgrade.\n"),
- .threshold_version = 903
- },
-
/*
* pg_upgrade only preserves these system values: pg_class.oid pg_type.oid
* pg_enum.oid
@@ -209,30 +188,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
.threshold_version = 1500
},
- /*
- * It's no longer allowed to create tables or views with "unknown"-type
- * columns. We do not complain about views with such columns, because
- * they should get silently converted to "text" columns during the DDL
- * dump and reload; it seems unlikely to be worth making users do that by
- * hand. However, if there's a table with such a column, the DDL reload
- * will fail, so we should pre-detect that rather than failing
- * mid-upgrade. Worse, if there's a matview with such a column, the DDL
- * reload will silently change it to "text" which won't match the on-disk
- * storage (which is like "cstring"). So we *must* reject that.
- */
- {
- .status = gettext_noop("Checking for invalid \"unknown\" user columns"),
- .report_filename = "tables_using_unknown.txt",
- .base_query =
- "SELECT 'pg_catalog.unknown'::pg_catalog.regtype AS oid",
- .report_text =
- gettext_noop("Your installation contains the \"unknown\" data type in user tables.\n"
- "This data type is no longer allowed in tables, so this cluster\n"
- "cannot currently be upgraded. You can drop the problem columns\n"
- "and restart the upgrade.\n"),
- .threshold_version = 906
- },
-
/*
* PG 12 changed the 'sql_identifier' type storage to be based on name,
* not varchar, which breaks on-disk format for existing data. So we need
@@ -255,23 +210,6 @@ static DataTypesUsageChecks data_types_usage_checks[] =
.threshold_version = 1100
},
- /*
- * JSONB changed its storage format during 9.4 beta, so check for it.
- */
- {
- .status = gettext_noop("Checking for incompatible \"jsonb\" data type in user tables"),
- .report_filename = "tables_using_jsonb.txt",
- .base_query =
- "SELECT 'pg_catalog.jsonb'::pg_catalog.regtype AS oid",
- .report_text =
- gettext_noop("Your installation contains the \"jsonb\" data type in user tables.\n"
- "The internal format of \"jsonb\" changed during 9.4 beta so this\n"
- "cluster cannot currently be upgraded. You can drop the problem \n"
- "columns and restart the upgrade.\n"),
- .threshold_version = MANUAL_CHECK,
- .version_hook = jsonb_9_4_check_applicable
- },
-
/*
* PG 12 removed types abstime, reltime, tinterval.
*/
@@ -712,20 +650,6 @@ check_and_dump_old_cluster(void)
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 1800)
check_for_gist_inet_ops(&old_cluster);
- /*
- * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged
- * hash indexes
- */
- if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
- {
- if (user_opts.check)
- old_9_6_invalidate_hash_indexes(&old_cluster, true);
- }
-
- /* 9.5 and below should not have roles starting with pg_ */
- if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905)
- check_for_pg_role_prefix(&old_cluster);
-
/*
* While not a check option, we do this now because this is the only time
* the old server is running.
@@ -772,20 +696,6 @@ check_new_cluster(void)
* system boundaries.
*/
check_hard_link(TRANSFER_MODE_SWAP);
-
- /*
- * There are a few known issues with using --swap to upgrade from
- * versions older than 10. For example, the sequence tuple format
- * changed in v10, and the visibility map format changed in 9.6.
- * While such problems are not insurmountable (and we may have to
- * deal with similar problems in the future, anyway), it doesn't
- * seem worth the effort to support swap mode for upgrades from
- * long-unsupported versions.
- */
- if (GET_MAJOR_VERSION(old_cluster.major_version) < 1000)
- pg_fatal("Swap mode can only upgrade clusters from PostgreSQL version %s and later.",
- "10");
-
break;
}
@@ -831,10 +741,6 @@ issue_warnings_and_set_wal_level(void)
*/
start_postmaster(&new_cluster, true);
- /* Reindex hash indexes for old < 10.0 */
- if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
- old_9_6_invalidate_hash_indexes(&new_cluster, false);
-
report_extension_updates(&new_cluster);
stop_postmaster(false);
@@ -892,9 +798,9 @@ check_cluster_versions(void)
* upgrades
*/
- if (GET_MAJOR_VERSION(old_cluster.major_version) < 902)
+ if (GET_MAJOR_VERSION(old_cluster.major_version) < 10)
pg_fatal("This utility can only upgrade from PostgreSQL version %s and later.",
- "9.2");
+ "10");
/* Only current PG version is supported as a target */
if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
@@ -1569,12 +1475,10 @@ check_for_incompatible_polymorphics(ClusterInfo *cluster)
", 'array_cat(anyarray,anyarray)'"
", 'array_prepend(anyelement,anyarray)'");
- if (GET_MAJOR_VERSION(cluster->major_version) >= 903)
appendPQExpBufferStr(&old_polymorphics,
", 'array_remove(anyarray,anyelement)'"
", 'array_replace(anyarray,anyelement,anyelement)'");
- if (GET_MAJOR_VERSION(cluster->major_version) >= 905)
appendPQExpBufferStr(&old_polymorphics,
", 'array_position(anyarray,anyelement)'"
", 'array_position(anyarray,anyelement,integer)'"
@@ -1870,63 +1774,6 @@ check_for_gist_inet_ops(ClusterInfo *cluster)
check_ok();
}
-/*
- * check_for_pg_role_prefix()
- *
- * Versions older than 9.6 should not have any pg_* roles
- */
-static void
-check_for_pg_role_prefix(ClusterInfo *cluster)
-{
- PGresult *res;
- PGconn *conn = connectToServer(cluster, "template1");
- int ntups;
- int i_roloid;
- int i_rolname;
- FILE *script = NULL;
- char output_path[MAXPGPATH];
-
- prep_status("Checking for roles starting with \"pg_\"");
-
- snprintf(output_path, sizeof(output_path), "%s/%s",
- log_opts.basedir,
- "pg_role_prefix.txt");
-
- res = executeQueryOrDie(conn,
- "SELECT oid AS roloid, rolname "
- "FROM pg_catalog.pg_roles "
- "WHERE rolname ~ '^pg_'");
-
- ntups = PQntuples(res);
- i_roloid = PQfnumber(res, "roloid");
- i_rolname = PQfnumber(res, "rolname");
- for (int rowno = 0; rowno < ntups; rowno++)
- {
- if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
- pg_fatal("could not open file \"%s\": %m", output_path);
- fprintf(script, "%s (oid=%s)\n",
- PQgetvalue(res, rowno, i_rolname),
- PQgetvalue(res, rowno, i_roloid));
- }
-
- PQclear(res);
-
- PQfinish(conn);
-
- if (script)
- {
- fclose(script);
- pg_log(PG_REPORT, "fatal");
- pg_fatal("Your installation contains roles starting with \"pg_\".\n"
- "\"pg_\" is a reserved prefix for system roles. The cluster\n"
- "cannot be upgraded until these roles are renamed.\n"
- "A list of roles starting with \"pg_\" is in the file:\n"
- " %s", output_path);
- }
- else
- check_ok();
-}
-
/*
* Callback function for processing results of query for
* check_for_user_defined_encoding_conversions()'s UpgradeTask. If the query
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index cffcd4b0eba..8188355240f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -602,14 +602,12 @@ get_control_data(ClusterInfo *cluster)
/* verify that we got all the mandatory pg_control data */
if (!got_xid || !got_oid ||
!got_multi || !got_oldestxid ||
- (!got_oldestmulti &&
- cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) ||
+ !got_oldestmulti ||
!got_mxoff || (!live_check && !got_nextxlogfile) ||
!got_float8_pass_by_value || !got_align || !got_blocksz ||
!got_largesz || !got_walsz || !got_walseg || !got_ident ||
!got_index || !got_toast ||
- (!got_large_object &&
- cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) ||
+ !got_large_object ||
!got_date_is_int || !got_data_checksum_version ||
(!got_default_char_signedness &&
cluster->controldata.cat_ver >= DEFAULT_CHAR_SIGNEDNESS_CAT_VER))
@@ -630,8 +628,7 @@ get_control_data(ClusterInfo *cluster)
if (!got_multi)
pg_log(PG_REPORT, " latest checkpoint next MultiXactId");
- if (!got_oldestmulti &&
- cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
+ if (!got_oldestmulti)
pg_log(PG_REPORT, " latest checkpoint oldest MultiXactId");
if (!got_oldestxid)
@@ -670,8 +667,7 @@ get_control_data(ClusterInfo *cluster)
if (!got_toast)
pg_log(PG_REPORT, " maximum TOAST chunk size");
- if (!got_large_object &&
- cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER)
+ if (!got_large_object)
pg_log(PG_REPORT, " large-object chunk size");
if (!got_date_is_int)
diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c
index 563b4a6b5fa..44355feea30 100644
--- a/src/bin/pg_upgrade/exec.c
+++ b/src/bin/pg_upgrade/exec.c
@@ -55,16 +55,7 @@ get_bin_version(ClusterInfo *cluster)
if (sscanf(cmd_output, "%*s %*s %d.%d", &v1, &v2) < 1)
pg_fatal("could not get pg_ctl version output from %s", cmd);
- if (v1 < 10)
- {
- /* old style, e.g. 9.6.1 */
- cluster->bin_version = v1 * 10000 + v2 * 100;
- }
- else
- {
- /* new style, e.g. 10.1 */
cluster->bin_version = v1 * 10000;
- }
}
@@ -353,17 +344,7 @@ check_data_dir(ClusterInfo *cluster)
check_single_dir(pg_data, "pg_subtrans");
check_single_dir(pg_data, PG_TBLSPC_DIR);
check_single_dir(pg_data, "pg_twophase");
-
- /* pg_xlog has been renamed to pg_wal in v10 */
- if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
- check_single_dir(pg_data, "pg_xlog");
- else
check_single_dir(pg_data, "pg_wal");
-
- /* pg_clog has been renamed to pg_xact in v10 */
- if (GET_MAJOR_VERSION(cluster->major_version) <= 906)
- check_single_dir(pg_data, "pg_clog");
- else
check_single_dir(pg_data, "pg_xact");
}
@@ -404,10 +385,6 @@ check_bin_dir(ClusterInfo *cluster, bool check_versions)
*/
get_bin_version(cluster);
- /* pg_resetxlog has been renamed to pg_resetwal in version 10 */
- if (GET_MAJOR_VERSION(cluster->bin_version) <= 906)
- check_exec(cluster->bindir, "pg_resetxlog", check_versions);
- else
check_exec(cluster->bindir, "pg_resetwal", check_versions);
if (cluster == &new_cluster)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index 5b276008614..af82c0de490 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -20,12 +20,8 @@
#include <linux/fs.h>
#endif
-#include "access/visibilitymapdefs.h"
#include "common/file_perm.h"
#include "pg_upgrade.h"
-#include "storage/bufpage.h"
-#include "storage/checksum.h"
-#include "storage/checksum_impl.h"
/*
@@ -196,166 +192,6 @@ linkFile(const char *src, const char *dst,
}
-/*
- * rewriteVisibilityMap()
- *
- * Transform a visibility map file, copying from src to dst.
- * schemaName/relName are relation's SQL name (used for error messages only).
- *
- * In versions of PostgreSQL prior to catversion 201603011, PostgreSQL's
- * visibility map included one bit per heap page; it now includes two.
- * When upgrading a cluster from before that time to a current PostgreSQL
- * version, we could refuse to copy visibility maps from the old cluster
- * to the new cluster; the next VACUUM would recreate them, but at the
- * price of scanning the entire table. So, instead, we rewrite the old
- * visibility maps in the new format. That way, the all-visible bits
- * remain set for the pages for which they were set previously. The
- * all-frozen bits are never set by this conversion; we leave that to VACUUM.
- */
-void
-rewriteVisibilityMap(const char *fromfile, const char *tofile,
- const char *schemaName, const char *relName)
-{
- int src_fd;
- int dst_fd;
- PGIOAlignedBlock buffer;
- PGIOAlignedBlock new_vmbuf;
- ssize_t totalBytesRead = 0;
- ssize_t src_filesize;
- int rewriteVmBytesPerPage;
- BlockNumber new_blkno = 0;
- struct stat statbuf;
-
- /* Compute number of old-format bytes per new page */
- rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
-
- if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
- pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %m",
- schemaName, relName, fromfile);
-
- if (fstat(src_fd, &statbuf) != 0)
- pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %m",
- schemaName, relName, fromfile);
-
- if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
- pg_file_create_mode)) < 0)
- pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %m",
- schemaName, relName, tofile);
-
- /* Save old file size */
- src_filesize = statbuf.st_size;
-
- /*
- * Turn each visibility map page into 2 pages one by one. Each new page
- * has the same page header as the old one. If the last section of the
- * last page is empty, we skip it, mostly to avoid turning one-page
- * visibility maps for small relations into two pages needlessly.
- */
- while (totalBytesRead < src_filesize)
- {
- ssize_t bytesRead;
- char *old_cur;
- char *old_break;
- char *old_blkend;
- PageHeaderData pageheader;
- bool old_lastblk;
-
- if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
- {
- if (bytesRead < 0)
- pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %m",
- schemaName, relName, fromfile);
- else
- pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"",
- schemaName, relName, fromfile);
- }
-
- totalBytesRead += BLCKSZ;
- old_lastblk = (totalBytesRead == src_filesize);
-
- /* Save the page header data */
- memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
-
- /*
- * These old_* variables point to old visibility map page. old_cur
- * points to current position on old page. old_blkend points to end of
- * old block. old_break is the end+1 position on the old page for the
- * data that will be transferred to the current new page.
- */
- old_cur = buffer.data + SizeOfPageHeaderData;
- old_blkend = buffer.data + bytesRead;
- old_break = old_cur + rewriteVmBytesPerPage;
-
- while (old_break <= old_blkend)
- {
- char *new_cur;
- bool empty = true;
- bool old_lastpart;
-
- /* First, copy old page header to new page */
- memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
-
- /* Rewriting the last part of the last old page? */
- old_lastpart = old_lastblk && (old_break == old_blkend);
-
- new_cur = new_vmbuf.data + SizeOfPageHeaderData;
-
- /* Process old page bytes one by one, and turn it into new page. */
- while (old_cur < old_break)
- {
- uint8 byte = *(uint8 *) old_cur;
- uint16 new_vmbits = 0;
- int i;
-
- /* Generate new format bits while keeping old information */
- for (i = 0; i < BITS_PER_BYTE; i++)
- {
- if (byte & (1 << i))
- {
- empty = false;
- new_vmbits |=
- VISIBILITYMAP_ALL_VISIBLE << (BITS_PER_HEAPBLOCK * i);
- }
- }
-
- /* Copy new visibility map bytes to new-format page */
- new_cur[0] = (char) (new_vmbits & 0xFF);
- new_cur[1] = (char) (new_vmbits >> 8);
-
- old_cur++;
- new_cur += BITS_PER_HEAPBLOCK;
- }
-
- /* If the last part of the last page is empty, skip writing it */
- if (old_lastpart && empty)
- break;
-
- /* Set new checksum for visibility map page, if enabled */
- if (new_cluster.controldata.data_checksum_version != PG_DATA_CHECKSUM_OFF)
- ((PageHeader) new_vmbuf.data)->pd_checksum =
- pg_checksum_page(new_vmbuf.data, new_blkno);
-
- errno = 0;
- if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
- {
- /* if write didn't set errno, assume problem is no disk space */
- if (errno == 0)
- errno = ENOSPC;
- pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %m",
- schemaName, relName, tofile);
- }
-
- /* Advance for next new page */
- old_break += rewriteVmBytesPerPage;
- new_blkno++;
- }
- }
-
- /* Clean up */
- close(dst_fd);
- close(src_fd);
-}
-
void
check_file_clone(void)
{
diff --git a/src/bin/pg_upgrade/multixact_rewrite.c b/src/bin/pg_upgrade/multixact_rewrite.c
index 823984ec8f3..c45b3183684 100644
--- a/src/bin/pg_upgrade/multixact_rewrite.c
+++ b/src/bin/pg_upgrade/multixact_rewrite.c
@@ -25,10 +25,7 @@ static void RecordMultiXactMembers(SlruSegState *members_writer,
* 32-bit offsets to the current format.
*
* Multixids in the range [from_multi, to_multi) are read from the old
- * cluster, and written in the new format. An important edge case is that if
- * from_multi == to_multi, this initializes the new pg_multixact files in the
- * new format without trying to open any old files. (We rely on that when
- * upgrading from PostgreSQL version 9.2 or below.)
+ * cluster, and written in the new format.
*
* Returns the new nextOffset value; the caller should set it in the new
* control file. The new members always start from offset 1, regardless of
@@ -42,6 +39,7 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
SlruSegState *members_writer;
char dir[MAXPGPATH] = {0};
bool prev_multixid_valid = false;
+ OldMultiXactReader *old_reader;
/*
* The range of valid multi XIDs is unchanged by the conversion (they are
@@ -63,10 +61,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
* Convert old multixids, if needed, by reading them one-by-one from the
* old cluster.
*/
- if (to_multi != from_multi)
- {
- OldMultiXactReader *old_reader;
-
old_reader = AllocOldMultiXactRead(old_cluster.pgdata,
old_cluster.controldata.chkpnt_nxtmulti,
old_cluster.controldata.chkpnt_nxtmxoff);
@@ -113,7 +107,6 @@ rewrite_multixacts(MultiXactId from_multi, MultiXactId to_multi)
}
FreeOldMultiXactReader(old_reader);
- }
/* Write the final 'next' offset to the last SLRU page */
RecordMultiXactOffset(offsets_writer, to_multi,
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 2127d297bfe..e5d7920c1b1 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -714,13 +714,6 @@ create_new_objects(void)
end_progress_output();
check_ok();
- /*
- * We don't have minmxids for databases or relations in pre-9.3 clusters,
- * so set those after we have restored the schema.
- */
- if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
- set_frozenxids(true);
-
/* update new_cluster info now that we have objects in the databases */
get_db_rel_and_slot_infos(&new_cluster);
}
@@ -777,10 +770,7 @@ copy_xact_xlog_xid(void)
* Copy old commit logs to new data dir. pg_clog has been renamed to
* pg_xact in post-10 clusters.
*/
- copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
- "pg_clog" : "pg_xact",
- GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
- "pg_clog" : "pg_xact");
+ copy_subdir_files("pg_xact", "pg_xact");
prep_status("Setting oldest XID for new cluster");
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
@@ -809,7 +799,6 @@ copy_xact_xlog_xid(void)
check_ok();
/* Copy or convert pg_multixact files */
- Assert(new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER);
Assert(new_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER);
if (old_cluster.controldata.cat_ver >= MULTIXACTOFFSET_FORMATCHANGE_CAT_VER)
{
@@ -844,25 +833,7 @@ copy_xact_xlog_xid(void)
* Determine the range of multixacts to convert.
*/
nxtmulti = old_cluster.controldata.chkpnt_nxtmulti;
- if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
- {
- /* Versions 9.3 - 18: convert all multixids */
oldstMulti = old_cluster.controldata.chkpnt_oldstMulti;
- }
- else
- {
- /*
- * In PostgreSQL 9.2 and below, multitransactions were only used
- * for row locking, and as such don't need to be preserved during
- * upgrade. In that case, we utilize rewrite_multixacts() just to
- * initialize new, empty files in the new format.
- *
- * It's important that the oldest multi is set to the latest value
- * used by the old system, so that multixact.c returns the empty
- * set for multis that might be present on disk.
- */
- oldstMulti = nxtmulti;
- }
/* handle wraparound */
if (nxtmulti < FirstMultiXactId)
nxtmulti = FirstMultiXactId;
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index ccd1ac0d013..d6e5bca5792 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -101,19 +101,6 @@ extern char *output_files[];
#endif
-/*
- * The format of visibility map was changed with this 9.6 commit.
- */
-#define VISIBILITY_MAP_FROZEN_BIT_CAT_VER 201603011
-
-/*
- * pg_multixact format changed in 9.3 commit 0ac5ad5134f2769ccbaefec73844f85,
- * ("Improve concurrency of foreign key locking") which also updated catalog
- * version to this value. pg_upgrade behavior depends on whether old and new
- * server versions are both newer than this, or only the new one is.
- */
-#define MULTIXACT_FORMATCHANGE_CAT_VER 201301231
-
/*
* MultiXactOffset was changed from 32-bit to 64-bit in version 19, at this
* catalog version. pg_multixact files need to be converted when upgrading
@@ -121,17 +108,6 @@ extern char *output_files[];
*/
#define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202512091
-/*
- * large object chunk size added to pg_controldata,
- * commit 5f93c37805e7485488480916b4585e098d3cc883
- */
-#define LARGE_OBJECT_SIZE_PG_CONTROL_VER 942
-
-/*
- * change in JSONB format during 9.4 beta
- */
-#define JSONB_FORMAT_CHANGE_CAT_VER 201409291
-
/*
* The control file was changed to have the default char signedness,
* commit 44fe30fdab6746a287163e7cc093fd36cda8eb92
@@ -429,8 +405,6 @@ void copyFileByRange(const char *src, const char *dst,
const char *schemaName, const char *relName);
void linkFile(const char *src, const char *dst,
const char *schemaName, const char *relName);
-void rewriteVisibilityMap(const char *fromfile, const char *tofile,
- const char *schemaName, const char *relName);
void check_file_clone(void);
void check_copy_file_range(void);
void check_hard_link(transferMode transfer_mode);
@@ -500,10 +474,7 @@ unsigned int str2uint(const char *str);
/* version.c */
-bool jsonb_9_4_check_applicable(ClusterInfo *cluster);
bool protocol_negotiation_supported(const ClusterInfo *cluster);
-void old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
- bool check_mode);
void report_extension_updates(ClusterInfo *cluster);
diff --git a/src/bin/pg_upgrade/relfilenumber.c b/src/bin/pg_upgrade/relfilenumber.c
index d5088447e0d..ec2ff7acb21 100644
--- a/src/bin/pg_upgrade/relfilenumber.c
+++ b/src/bin/pg_upgrade/relfilenumber.c
@@ -18,7 +18,7 @@
#include "pg_upgrade.h"
static void transfer_single_new_db(FileNameMap *maps, int size, char *old_tablespace, char *new_tablespace);
-static void transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit);
+static void transfer_relfile(FileNameMap *map, const char *type_suffix);
/*
* The following set of sync_queue_* functions are used for --swap to reduce
@@ -496,25 +496,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
char *old_tablespace, char *new_tablespace)
{
int mapnum;
- bool vm_must_add_frozenbit = false;
-
- /*
- * Do we need to rewrite visibilitymap?
- */
- if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_FROZEN_BIT_CAT_VER &&
- new_cluster.controldata.cat_ver >= VISIBILITY_MAP_FROZEN_BIT_CAT_VER)
- vm_must_add_frozenbit = true;
/* --swap has its own subroutine */
if (user_opts.transfer_mode == TRANSFER_MODE_SWAP)
{
- /*
- * We don't support --swap to upgrade from versions that require
- * rewriting the visibility map. We should've failed already if
- * someone tries to do that.
- */
- Assert(!vm_must_add_frozenbit);
-
do_swap(maps, size, old_tablespace, new_tablespace);
return;
}
@@ -525,13 +510,13 @@ transfer_single_new_db(FileNameMap *maps, int size,
strcmp(maps[mapnum].old_tablespace, old_tablespace) == 0)
{
/* transfer primary file */
- transfer_relfile(&maps[mapnum], "", vm_must_add_frozenbit);
+ transfer_relfile(&maps[mapnum], "");
/*
* Copy/link any fsm and vm files, if they exist
*/
- transfer_relfile(&maps[mapnum], "_fsm", vm_must_add_frozenbit);
- transfer_relfile(&maps[mapnum], "_vm", vm_must_add_frozenbit);
+ transfer_relfile(&maps[mapnum], "_fsm");
+ transfer_relfile(&maps[mapnum], "_vm");
}
}
}
@@ -540,12 +525,10 @@ transfer_single_new_db(FileNameMap *maps, int size,
/*
* transfer_relfile()
*
- * Copy or link file from old cluster to new one. If vm_must_add_frozenbit
- * is true, visibility map forks are converted and rewritten, even in link
- * mode.
+ * Copy or link file from old cluster to new one.
*/
static void
-transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_frozenbit)
+transfer_relfile(FileNameMap *map, const char *type_suffix)
{
char old_file[MAXPGPATH];
char new_file[MAXPGPATH];
@@ -604,14 +587,6 @@ transfer_relfile(FileNameMap *map, const char *type_suffix, bool vm_must_add_fro
/* Copying files might take some time, so give feedback. */
pg_log(PG_STATUS, "%s", old_file);
- if (vm_must_add_frozenbit && strcmp(type_suffix, "_vm") == 0)
- {
- /* Need to rewrite visibility map format */
- pg_log(PG_VERBOSE, "rewriting \"%s\" to \"%s\"",
- old_file, new_file);
- rewriteVisibilityMap(old_file, new_file, map->nspname, map->relname);
- }
- else
switch (user_opts.transfer_mode)
{
case TRANSFER_MODE_CLONE:
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index 047670d4acb..9e83d4659be 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -12,22 +12,6 @@
#include "fe_utils/string_utils.h"
#include "pg_upgrade.h"
-/*
- * version_hook functions for check_for_data_types_usage in order to determine
- * whether a data type check should be executed for the cluster in question or
- * not.
- */
-bool
-jsonb_9_4_check_applicable(ClusterInfo *cluster)
-{
- /* JSONB changed its storage format during 9.4 beta */
- if (GET_MAJOR_VERSION(cluster->major_version) == 904 &&
- cluster->controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER)
- return true;
-
- return false;
-}
-
/*
* Older servers can't support newer protocol versions, so their connection
* strings will need to lock max_protocol_version to 3.0.
@@ -46,117 +30,6 @@ protocol_negotiation_supported(const ClusterInfo *cluster)
return (GET_MAJOR_VERSION(cluster->major_version) >= 1100);
}
-/*
- * old_9_6_invalidate_hash_indexes()
- * 9.6 -> 10
- * Hash index binary format has changed from 9.6->10.0
- */
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
-{
- int dbnum;
- FILE *script = NULL;
- bool found = false;
- char *output_path = "reindex_hash.sql";
-
- prep_status("Checking for hash indexes");
-
- for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
- {
- PGresult *res;
- bool db_used = false;
- int ntups;
- int rowno;
- int i_nspname,
- i_relname;
- DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
- PGconn *conn = connectToServer(cluster, active_db->db_name);
-
- /* find hash indexes */
- res = executeQueryOrDie(conn,
- "SELECT n.nspname, c.relname "
- "FROM pg_catalog.pg_class c, "
- " pg_catalog.pg_index i, "
- " pg_catalog.pg_am a, "
- " pg_catalog.pg_namespace n "
- "WHERE i.indexrelid = c.oid AND "
- " c.relam = a.oid AND "
- " c.relnamespace = n.oid AND "
- " a.amname = 'hash'"
- );
-
- ntups = PQntuples(res);
- i_nspname = PQfnumber(res, "nspname");
- i_relname = PQfnumber(res, "relname");
- for (rowno = 0; rowno < ntups; rowno++)
- {
- found = true;
- if (!check_mode)
- {
- if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
- pg_fatal("could not open file \"%s\": %m", output_path);
- if (!db_used)
- {
- PQExpBufferData connectbuf;
-
- initPQExpBuffer(&connectbuf);
- appendPsqlMetaConnect(&connectbuf, active_db->db_name);
- fputs(connectbuf.data, script);
- termPQExpBuffer(&connectbuf);
- db_used = true;
- }
- fprintf(script, "REINDEX INDEX %s.%s;\n",
- quote_identifier(PQgetvalue(res, rowno, i_nspname)),
- quote_identifier(PQgetvalue(res, rowno, i_relname)));
- }
- }
-
- PQclear(res);
-
- if (!check_mode && db_used)
- {
- /* mark hash indexes as invalid */
- PQclear(executeQueryOrDie(conn,
- "UPDATE pg_catalog.pg_index i "
- "SET indisvalid = false "
- "FROM pg_catalog.pg_class c, "
- " pg_catalog.pg_am a, "
- " pg_catalog.pg_namespace n "
- "WHERE i.indexrelid = c.oid AND "
- " c.relam = a.oid AND "
- " c.relnamespace = n.oid AND "
- " a.amname = 'hash'"));
- }
-
- PQfinish(conn);
- }
-
- if (script)
- fclose(script);
-
- if (found)
- {
- report_status(PG_WARNING, "warning");
- if (check_mode)
- pg_log(PG_WARNING, "\n"
- "Your installation contains hash indexes. These indexes have different\n"
- "internal formats between your old and new clusters, so they must be\n"
- "reindexed with the REINDEX command. After upgrading, you will be given\n"
- "REINDEX instructions.");
- else
- pg_log(PG_WARNING, "\n"
- "Your installation contains hash indexes. These indexes have different\n"
- "internal formats between your old and new clusters, so they must be\n"
- "reindexed with the REINDEX command. The file\n"
- " %s\n"
- "when executed by psql by the database superuser will recreate all invalid\n"
- "indexes; until then, none of these indexes will be used.",
- output_path);
- }
- else
- check_ok();
-}
-
/*
* Callback function for processing results of query for
* report_extension_updates()'s UpgradeTask. If the query returned any rows,
--
2.50.1 (Apple Git-155)
--2xrO7hiVfr8aWtYg
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
filename=v4-0003-psql-bump-minimum-supported-version-to-v10.patch
^ permalink raw reply [nested|flat] 11+ messages in thread
end of thread, other threads:[~2026-04-17 18:20 UTC | newest]
Thread overview: 11+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2024-01-01 13:55 Re: Shared detoast Datum proposal Andy Fan <[email protected]>
2024-01-06 15:12 ` vignesh C <[email protected]>
2024-01-07 01:10 ` Andy Fan <[email protected]>
2024-01-08 09:52 ` Andy Fan <[email protected]>
2024-01-22 06:41 ` Peter Smith <[email protected]>
2024-01-23 05:44 ` Andy Fan <[email protected]>
2024-01-23 08:59 ` Michael Zhilin <[email protected]>
2024-01-23 19:18 ` Andy Fan <[email protected]>
2024-02-20 06:20 ` Andy Fan <[email protected]>
2024-02-20 18:15 ` Tomas Vondra <[email protected]>
2026-04-17 18:20 [PATCH v4 2/4] pg_upgrade: bump minimum supported version to v10 Nathan Bossart <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox