aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/execExpr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/execExpr.c')
-rw-r--r--src/backend/executor/execExpr.c155
1 files changed, 155 insertions, 0 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e0eb96fd5ad..81714341f07 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -3979,6 +3979,161 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
}
/*
+ * Build an ExprState that calls the given hash function(s) on the attnums
+ * given by 'keyColIdx' . When numCols > 1, the hash values returned by each
+ * hash function are combined to produce a single hash value.
+ *
+ * desc: tuple descriptor for the to-be-hashed columns
+ * ops: TupleTableSlotOps to use for the give TupleDesc
+ * hashfunctions: FmgrInfos for each hash function to call, one per numCols.
+ * These are used directly in the returned ExprState so must remain allocated.
+ * collations: collation to use when calling the hash function.
+ * numCols: array length of hashfunctions, collations and keyColIdx.
+ * parent: PlanState node that the resulting ExprState will be evaluated at
+ * init_value: Normally 0, but can be set to other values to seed the hash
+ * with. Non-zero is marginally slower, so best to only use if it's provably
+ * worthwhile.
+ */
+ExprState *
+ExecBuildHash32FromAttrs(TupleDesc desc, const TupleTableSlotOps *ops,
+ FmgrInfo *hashfunctions, Oid *collations,
+ int numCols, AttrNumber *keyColIdx,
+ PlanState *parent, uint32 init_value)
+{
+ ExprState *state = makeNode(ExprState);
+ ExprEvalStep scratch = {0};
+ NullableDatum *iresult = NULL;
+ intptr_t opcode;
+ AttrNumber last_attnum = 0;
+
+ Assert(numCols >= 0);
+
+ state->parent = parent;
+
+ /*
+ * Make a place to store intermediate hash values between subsequent
+ * hashing of individual columns. We only need this if there is more than
+ * one column to hash or an initial value plus one column.
+ */
+ if ((int64) numCols + (init_value != 0) > 1)
+ iresult = palloc(sizeof(NullableDatum));
+
+ /* find the highest attnum so we deform the tuple to that point */
+ for (int i = 0; i < numCols; i++)
+ last_attnum = Max(last_attnum, keyColIdx[i]);
+
+ scratch.opcode = EEOP_INNER_FETCHSOME;
+ scratch.d.fetch.last_var = last_attnum;
+ scratch.d.fetch.fixed = false;
+ scratch.d.fetch.kind = ops;
+ scratch.d.fetch.known_desc = desc;
+ if (ExecComputeSlotInfo(state, &scratch))
+ ExprEvalPushStep(state, &scratch);
+
+ if (init_value == 0)
+ {
+ /*
+ * No initial value, so we can assign the result of the hash function
+ * for the first attribute without having to concern ourselves with
+ * combining the result with any initial value.
+ */
+ opcode = EEOP_HASHDATUM_FIRST;
+ }
+ else
+ {
+ /*
+ * Set up operation to set the initial value. Normally we store this
+ * in the intermediate hash value location, but if there are no
+ * columns to hash, store it in the ExprState's result field.
+ */
+ scratch.opcode = EEOP_HASHDATUM_SET_INITVAL;
+ scratch.d.hashdatum_initvalue.init_value = UInt32GetDatum(init_value);
+ scratch.resvalue = numCols > 0 ? &iresult->value : &state->resvalue;
+ scratch.resnull = numCols > 0 ? &iresult->isnull : &state->resnull;
+
+ ExprEvalPushStep(state, &scratch);
+
+ /*
+ * When using an initial value use the NEXT32 ops as the FIRST ops
+ * would overwrite the stored initial value.
+ */
+ opcode = EEOP_HASHDATUM_NEXT32;
+ }
+
+ for (int i = 0; i < numCols; i++)
+ {
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo;
+ Oid inputcollid = collations[i];
+ AttrNumber attnum = keyColIdx[i] - 1;
+
+ finfo = &hashfunctions[i];
+ fcinfo = palloc0(SizeForFunctionCallInfo(1));
+
+ /* Initialize function call parameter structure too */
+ InitFunctionCallInfoData(*fcinfo, finfo, 1, inputcollid, NULL, NULL);
+
+ /*
+ * Fetch inner Var for this attnum and store it in the 1st arg of the
+ * hash func.
+ */
+ scratch.opcode = EEOP_INNER_VAR;
+ scratch.resvalue = &fcinfo->args[0].value;
+ scratch.resnull = &fcinfo->args[0].isnull;
+ scratch.d.var.attnum = attnum;
+ scratch.d.var.vartype = TupleDescAttr(desc, attnum)->atttypid;
+
+ ExprEvalPushStep(state, &scratch);
+
+ /* Call the hash function */
+ scratch.opcode = opcode;
+
+ if (i == numCols - 1)
+ {
+ /*
+ * The result for hashing the final column is stored in the
+ * ExprState.
+ */
+ scratch.resvalue = &state->resvalue;
+ scratch.resnull = &state->resnull;
+ }
+ else
+ {
+ Assert(iresult != NULL);
+
+ /* intermediate values are stored in an intermediate result */
+ scratch.resvalue = &iresult->value;
+ scratch.resnull = &iresult->isnull;
+ }
+
+ /*
+ * NEXT32 opcodes need to look at the intermediate result. We might
+ * as well just set this for all ops. FIRSTs won't look at it.
+ */
+ scratch.d.hashdatum.iresult = iresult;
+
+ scratch.d.hashdatum.finfo = finfo;
+ scratch.d.hashdatum.fcinfo_data = fcinfo;
+ scratch.d.hashdatum.fn_addr = finfo->fn_addr;
+ scratch.d.hashdatum.jumpdone = -1;
+
+ ExprEvalPushStep(state, &scratch);
+
+ /* subsequent attnums must be combined with the previous */
+ opcode = EEOP_HASHDATUM_NEXT32;
+ }
+
+ scratch.resvalue = NULL;
+ scratch.resnull = NULL;
+ scratch.opcode = EEOP_DONE;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecReadyExpr(state);
+
+ return state;
+}
+
+/*
* Build an ExprState that calls the given hash function(s) on the given
* 'hash_exprs'. When multiple expressions are present, the hash values
* returned by each hash function are combined to produce a single hash value.