Skip to content

Commit 87f81a5

Browse files
committed
Fix hypothetical bug in ExprState building for hashing
adf97c1 gave ExprStates the ability to hash expressions and return a single hash value. That commit supports seeding the hash value with an initial value to have that blended into the final hash value. Here we fix a hypothetical bug where if there are zero expressions to hash, the initial value is stored in the wrong location. The existing code stored the initial value in an intermediate location expecting that when the expressions were hashed that those steps would store the final hash value in the ExprState.resvalue field. However, that wouldn't happen when there are zero expressions to hash. The correct thing to do instead is to have a special case for zero expressions and when we hit that case, store the initial value directly in the ExprState.resvalue. The reason that this is a hypothetical bug is that no code currently calls ExecBuildHash32Expr passing a non-zero initial value. Discussion: https://postgr.es/m/CAApHDvpMAL_zxbMRr1LOex3O7Y7R7ZN2i8iUFLQhqQiJMAg3qw@mail.gmail.com
1 parent 7bdaa4b commit 87f81a5

File tree

1 file changed

+14
-9
lines changed

1 file changed

+14
-9
lines changed

src/backend/executor/execExpr.c

+14-9
Original file line numberDiff line numberDiff line change
@@ -4004,20 +4004,21 @@ ExecBuildHash32Expr(TupleDesc desc, const TupleTableSlotOps *ops,
40044004
ListCell *lc2;
40054005
intptr_t strict_opcode;
40064006
intptr_t opcode;
4007+
int num_exprs = list_length(hash_exprs);
40074008

4008-
Assert(list_length(hash_exprs) == list_length(collations));
4009+
Assert(num_exprs == list_length(collations));
40094010

40104011
state->parent = parent;
40114012

40124013
/* Insert setup steps as needed. */
40134014
ExecCreateExprSetupSteps(state, (Node *) hash_exprs);
40144015

40154016
/*
4016-
* When hashing more than 1 expression or if we have an init value, we
4017-
* need somewhere to store the intermediate hash value so that it's
4018-
* available to be combined with the result of subsequent hashing.
4017+
* Make a place to store intermediate hash values between subsequent
4018+
* hashing of individual expressions. We only need this if there is more
4019+
* than one expression to hash or an initial value plus one expression.
40194020
*/
4020-
if (list_length(hash_exprs) > 1 || init_value != 0)
4021+
if ((int64) num_exprs + (init_value != 0) > 1)
40214022
iresult = palloc(sizeof(NullableDatum));
40224023

40234024
if (init_value == 0)
@@ -4032,11 +4033,15 @@ ExecBuildHash32Expr(TupleDesc desc, const TupleTableSlotOps *ops,
40324033
}
40334034
else
40344035
{
4035-
/* Set up operation to set the initial value. */
4036+
/*
4037+
* Set up operation to set the initial value. Normally we store this
4038+
* in the intermediate hash value location, but if there are no exprs
4039+
* to hash, store it in the ExprState's result field.
4040+
*/
40364041
scratch.opcode = EEOP_HASHDATUM_SET_INITVAL;
40374042
scratch.d.hashdatum_initvalue.init_value = UInt32GetDatum(init_value);
4038-
scratch.resvalue = &iresult->value;
4039-
scratch.resnull = &iresult->isnull;
4043+
scratch.resvalue = num_exprs > 0 ? &iresult->value : &state->resvalue;
4044+
scratch.resnull = num_exprs > 0 ? &iresult->isnull : &state->resnull;
40404045

40414046
ExprEvalPushStep(state, &scratch);
40424047

@@ -4074,7 +4079,7 @@ ExecBuildHash32Expr(TupleDesc desc, const TupleTableSlotOps *ops,
40744079
&fcinfo->args[0].value,
40754080
&fcinfo->args[0].isnull);
40764081

4077-
if (i == list_length(hash_exprs) - 1)
4082+
if (i == num_exprs - 1)
40784083
{
40794084
/* the result for hashing the final expr is stored in the state */
40804085
scratch.resvalue = &state->resvalue;

0 commit comments

Comments
 (0)