aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/execExpr.c35
-rw-r--r--src/backend/executor/execExprInterp.c16
-rw-r--r--src/backend/jit/llvm/llvmjit_expr.c18
-rw-r--r--src/include/executor/execExpr.h1
-rw-r--r--src/test/regress/expected/join.out55
-rw-r--r--src/test/regress/sql/join.sql21
6 files changed, 129 insertions, 17 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index c8077aa57bd..a343d0bc6a2 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -3996,6 +3996,7 @@ ExecBuildHash32Expr(TupleDesc desc, const TupleTableSlotOps *ops,
{
ExprState *state = makeNode(ExprState);
ExprEvalStep scratch = {0};
+ NullableDatum *iresult = NULL;
List *adjust_jumps = NIL;
ListCell *lc;
ListCell *lc2;
@@ -4009,6 +4010,14 @@ ExecBuildHash32Expr(TupleDesc desc, const TupleTableSlotOps *ops,
/* Insert setup steps as needed. */
ExecCreateExprSetupSteps(state, (Node *) hash_exprs);
+ /*
+ * When hashing more than 1 expression or if we have an init value, we
+ * need somewhere to store the intermediate hash value so that it's
+ * available to be combined with the result of subsequent hashing.
+ */
+ if (list_length(hash_exprs) > 1 || init_value != 0)
+ iresult = palloc(sizeof(NullableDatum));
+
if (init_value == 0)
{
/*
@@ -4024,8 +4033,8 @@ ExecBuildHash32Expr(TupleDesc desc, const TupleTableSlotOps *ops,
/* Set up operation to set the initial value. */
scratch.opcode = EEOP_HASHDATUM_SET_INITVAL;
scratch.d.hashdatum_initvalue.init_value = UInt32GetDatum(init_value);
- scratch.resvalue = &state->resvalue;
- scratch.resnull = &state->resnull;
+ scratch.resvalue = &iresult->value;
+ scratch.resnull = &iresult->isnull;
ExprEvalPushStep(state, &scratch);
@@ -4063,8 +4072,26 @@ ExecBuildHash32Expr(TupleDesc desc, const TupleTableSlotOps *ops,
&fcinfo->args[0].value,
&fcinfo->args[0].isnull);
- scratch.resvalue = &state->resvalue;
- scratch.resnull = &state->resnull;
+ if (i == list_length(hash_exprs) - 1)
+ {
+ /* the result for hashing the final expr is stored in the state */
+ 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;
/* Initialize function call parameter structure too */
InitFunctionCallInfoData(*fcinfo, finfo, 1, inputcollid, NULL, NULL);
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 9fd988cc992..6a7f18f6ded 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -1600,10 +1600,11 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_CASE(EEOP_HASHDATUM_NEXT32)
{
FunctionCallInfo fcinfo = op->d.hashdatum.fcinfo_data;
- uint32 existing_hash = DatumGetUInt32(*op->resvalue);
+ uint32 existinghash;
+ existinghash = DatumGetUInt32(op->d.hashdatum.iresult->value);
/* combine successive hash values by rotating */
- existing_hash = pg_rotate_left32(existing_hash, 1);
+ existinghash = pg_rotate_left32(existinghash, 1);
/* leave the hash value alone on NULL inputs */
if (!fcinfo->args[0].isnull)
@@ -1612,10 +1613,10 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
/* execute hash func and combine with previous hash value */
hashvalue = DatumGetUInt32(op->d.hashdatum.fn_addr(fcinfo));
- existing_hash = existing_hash ^ hashvalue;
+ existinghash = existinghash ^ hashvalue;
}
- *op->resvalue = UInt32GetDatum(existing_hash);
+ *op->resvalue = UInt32GetDatum(existinghash);
*op->resnull = false;
EEO_NEXT();
@@ -1638,15 +1639,16 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
}
else
{
- uint32 existing_hash = DatumGetUInt32(*op->resvalue);
+ uint32 existinghash;
uint32 hashvalue;
+ existinghash = DatumGetUInt32(op->d.hashdatum.iresult->value);
/* combine successive hash values by rotating */
- existing_hash = pg_rotate_left32(existing_hash, 1);
+ existinghash = pg_rotate_left32(existinghash, 1);
/* execute hash func and combine with previous hash value */
hashvalue = DatumGetUInt32(op->d.hashdatum.fn_addr(fcinfo));
- *op->resvalue = UInt32GetDatum(existing_hash ^ hashvalue);
+ *op->resvalue = UInt32GetDatum(existinghash ^ hashvalue);
*op->resnull = false;
}
diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c
index 48ccdb942a2..0b3b5748ea2 100644
--- a/src/backend/jit/llvm/llvmjit_expr.c
+++ b/src/backend/jit/llvm/llvmjit_expr.c
@@ -1940,13 +1940,16 @@ llvm_compile_expr(ExprState *state)
{
LLVMValueRef v_tmp1;
LLVMValueRef v_tmp2;
+ LLVMValueRef tmp;
+
+ tmp = l_ptr_const(&op->d.hashdatum.iresult->value,
+ l_ptr(TypeSizeT));
/*
* Fetch the previously hashed value from where the
- * EEOP_HASHDATUM_FIRST operation stored it.
+ * previous hash operation stored it.
*/
- v_prevhash = l_load(b, TypeSizeT, v_resvaluep,
- "prevhash");
+ v_prevhash = l_load(b, TypeSizeT, tmp, "prevhash");
/*
* Rotate bits left by 1 bit. Be careful not to
@@ -2062,13 +2065,16 @@ llvm_compile_expr(ExprState *state)
{
LLVMValueRef v_tmp1;
LLVMValueRef v_tmp2;
+ LLVMValueRef tmp;
+
+ tmp = l_ptr_const(&op->d.hashdatum.iresult->value,
+ l_ptr(TypeSizeT));
/*
* Fetch the previously hashed value from where the
- * EEOP_HASHDATUM_FIRST_STRICT operation stored it.
+ * previous hash operation stored it.
*/
- v_prevhash = l_load(b, TypeSizeT, v_resvaluep,
- "prevhash");
+ v_prevhash = l_load(b, TypeSizeT, tmp, "prevhash");
/*
* Rotate bits left by 1 bit. Be careful not to
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index eec0aa699e5..cd97dfa0624 100644
--- a/src/include/executor/execExpr.h
+++ b/src/include/executor/execExpr.h
@@ -580,6 +580,7 @@ typedef struct ExprEvalStep
/* faster to access without additional indirection: */
PGFunction fn_addr; /* actual call address */
int jumpdone; /* jump here on null */
+ NullableDatum *iresult; /* intermediate hash result */
} hashdatum;
/* for EEOP_CONVERT_ROWTYPE */
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 756c2e24965..5669ed929a7 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -2359,6 +2359,61 @@ where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous;
(0 rows)
--
+-- Test hash joins with multiple hash keys and subplans.
+--
+-- First ensure we get a hash join with multiple hash keys.
+explain (costs off)
+select t1.unique1,t2.unique1 from tenk1 t1
+inner join tenk1 t2 on t1.two = t2.two
+ and t1.unique1 = (select min(unique1) from tenk1
+ where t2.unique1=unique1)
+where t1.unique1 < 10 and t2.unique1 < 10
+order by t1.unique1;
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------
+ Sort
+ Sort Key: t1.unique1
+ -> Hash Join
+ Hash Cond: ((t1.two = t2.two) AND (t1.unique1 = (SubPlan 2)))
+ -> Bitmap Heap Scan on tenk1 t1
+ Recheck Cond: (unique1 < 10)
+ -> Bitmap Index Scan on tenk1_unique1
+ Index Cond: (unique1 < 10)
+ -> Hash
+ -> Bitmap Heap Scan on tenk1 t2
+ Recheck Cond: (unique1 < 10)
+ -> Bitmap Index Scan on tenk1_unique1
+ Index Cond: (unique1 < 10)
+ SubPlan 2
+ -> Result
+ InitPlan 1
+ -> Limit
+ -> Index Only Scan using tenk1_unique1 on tenk1
+ Index Cond: ((unique1 IS NOT NULL) AND (unique1 = t2.unique1))
+(19 rows)
+
+-- Ensure we get the expected result
+select t1.unique1,t2.unique1 from tenk1 t1
+inner join tenk1 t2 on t1.two = t2.two
+ and t1.unique1 = (select min(unique1) from tenk1
+ where t2.unique1=unique1)
+where t1.unique1 < 10 and t2.unique1 < 10
+order by t1.unique1;
+ unique1 | unique1
+---------+---------
+ 0 | 0
+ 1 | 1
+ 2 | 2
+ 3 | 3
+ 4 | 4
+ 5 | 5
+ 6 | 6
+ 7 | 7
+ 8 | 8
+ 9 | 9
+(10 rows)
+
+--
-- checks for correct handling of quals in multiway outer joins
--
explain (costs off)
diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql
index 0c65e5af4be..73474bb64f5 100644
--- a/src/test/regress/sql/join.sql
+++ b/src/test/regress/sql/join.sql
@@ -442,6 +442,27 @@ select a.f1, b.f1, t.thousand, t.tenthous from
where b.f1 = t.thousand and a.f1 = b.f1 and (a.f1+b.f1+999) = t.tenthous;
--
+-- Test hash joins with multiple hash keys and subplans.
+--
+
+-- First ensure we get a hash join with multiple hash keys.
+explain (costs off)
+select t1.unique1,t2.unique1 from tenk1 t1
+inner join tenk1 t2 on t1.two = t2.two
+ and t1.unique1 = (select min(unique1) from tenk1
+ where t2.unique1=unique1)
+where t1.unique1 < 10 and t2.unique1 < 10
+order by t1.unique1;
+
+-- Ensure we get the expected result
+select t1.unique1,t2.unique1 from tenk1 t1
+inner join tenk1 t2 on t1.two = t2.two
+ and t1.unique1 = (select min(unique1) from tenk1
+ where t2.unique1=unique1)
+where t1.unique1 < 10 and t2.unique1 < 10
+order by t1.unique1;
+
+--
-- checks for correct handling of quals in multiway outer joins
--
explain (costs off)