diff options
author | David Rowley <drowley@postgresql.org> | 2021-07-07 16:29:17 +1200 |
---|---|---|
committer | David Rowley <drowley@postgresql.org> | 2021-07-07 16:29:17 +1200 |
commit | 29f45e299e7ffa1df0db44b8452228625479487f (patch) | |
tree | 948f286a1db23d164aeb20d4cb3d172ed986e758 /src/backend/executor | |
parent | d854720df6df68cfe1432342e33c9e3020572a51 (diff) | |
download | postgresql-29f45e299e7ffa1df0db44b8452228625479487f.tar.gz postgresql-29f45e299e7ffa1df0db44b8452228625479487f.zip |
Use a hash table to speed up NOT IN(values)
Similar to 50e17ad28, which allowed hash tables to be used for IN clauses
with a set of constants, here we add the same feature for NOT IN clauses.
NOT IN evaluates the same as: WHERE a <> v1 AND a <> v2 AND a <> v3.
Obviously, if we're using a hash table we must be exactly equivalent to
that and return the same result taking into account that either side of
the condition could contain a NULL. This requires a little bit of
special handling to make work with the hash table version.
When processing NOT IN, the ScalarArrayOpExpr's operator will be the <>
operator. To be able to build and lookup a hash table we must use the
<>'s negator operator. The planner checks if that exists and is hashable
and sets the relevant fields in ScalarArrayOpExpr to instruct the executor
to use hashing.
Author: David Rowley, James Coleman
Reviewed-by: James Coleman, Zhihong Yu
Discussion: https://postgr.es/m/CAApHDvoF1mum_FRk6D621edcB6KSHBi2+GAgWmioj5AhOu2vwQ@mail.gmail.com
Diffstat (limited to 'src/backend/executor')
-rw-r--r-- | src/backend/executor/execExpr.c | 24 | ||||
-rw-r--r-- | src/backend/executor/execExprInterp.c | 17 |
2 files changed, 35 insertions, 6 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 8c9f8a6aeb6..2c8c414a14f 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -1205,19 +1205,34 @@ ExecInitExprRec(Expr *node, ExprState *state, AclResult aclresult; FmgrInfo *hash_finfo; FunctionCallInfo hash_fcinfo; + Oid cmpfuncid; + + /* + * Select the correct comparison function. When we do hashed + * NOT IN clauses, the opfuncid will be the inequality + * comparison function and negfuncid will be set to equality. + * We need to use the equality function for hash probes. + */ + if (OidIsValid(opexpr->negfuncid)) + { + Assert(OidIsValid(opexpr->hashfuncid)); + cmpfuncid = opexpr->negfuncid; + } + else + cmpfuncid = opexpr->opfuncid; Assert(list_length(opexpr->args) == 2); scalararg = (Expr *) linitial(opexpr->args); arrayarg = (Expr *) lsecond(opexpr->args); /* Check permission to call function */ - aclresult = pg_proc_aclcheck(opexpr->opfuncid, + aclresult = pg_proc_aclcheck(cmpfuncid, GetUserId(), ACL_EXECUTE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, OBJECT_FUNCTION, - get_func_name(opexpr->opfuncid)); - InvokeFunctionExecuteHook(opexpr->opfuncid); + get_func_name(cmpfuncid)); + InvokeFunctionExecuteHook(cmpfuncid); if (OidIsValid(opexpr->hashfuncid)) { @@ -1233,7 +1248,7 @@ ExecInitExprRec(Expr *node, ExprState *state, /* Set up the primary fmgr lookup information */ finfo = palloc0(sizeof(FmgrInfo)); fcinfo = palloc0(SizeForFunctionCallInfo(2)); - fmgr_info(opexpr->opfuncid, finfo); + fmgr_info(cmpfuncid, finfo); fmgr_info_set_expr((Node *) node, finfo); InitFunctionCallInfoData(*fcinfo, finfo, 2, opexpr->inputcollid, NULL, NULL); @@ -1274,6 +1289,7 @@ ExecInitExprRec(Expr *node, ExprState *state, /* And perform the operation */ scratch.opcode = EEOP_HASHED_SCALARARRAYOP; + scratch.d.hashedscalararrayop.inclause = opexpr->useOr; scratch.d.hashedscalararrayop.finfo = finfo; scratch.d.hashedscalararrayop.fcinfo_data = fcinfo; scratch.d.hashedscalararrayop.fn_addr = finfo->fn_addr; diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 5483dee6507..eb49817cee4 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -3493,6 +3493,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco { ScalarArrayOpExprHashTable *elements_tab = op->d.hashedscalararrayop.elements_tab; FunctionCallInfo fcinfo = op->d.hashedscalararrayop.fcinfo_data; + bool inclause = op->d.hashedscalararrayop.inclause; bool strictfunc = op->d.hashedscalararrayop.finfo->fn_strict; Datum scalar = fcinfo->args[0].value; bool scalar_isnull = fcinfo->args[0].isnull; @@ -3596,7 +3597,12 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco /* Check the hash to see if we have a match. */ hashfound = NULL != saophash_lookup(elements_tab->hashtab, scalar); - result = BoolGetDatum(hashfound); + /* the result depends on if the clause is an IN or NOT IN clause */ + if (inclause) + result = BoolGetDatum(hashfound); /* IN */ + else + result = BoolGetDatum(!hashfound); /* NOT IN */ + resultnull = false; /* @@ -3605,7 +3611,7 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco * hashtable, but instead marked if we found any when building the table * in has_nulls. */ - if (!DatumGetBool(result) && op->d.hashedscalararrayop.has_nulls) + if (!hashfound && op->d.hashedscalararrayop.has_nulls) { if (strictfunc) { @@ -3633,6 +3639,13 @@ ExecEvalHashedScalarArrayOp(ExprState *state, ExprEvalStep *op, ExprContext *eco result = op->d.hashedscalararrayop.fn_addr(fcinfo); resultnull = fcinfo->isnull; + + /* + * Reverse the result for NOT IN clauses since the above function + * is the equality function and we need not-equals. + */ + if (!inclause) + result = !result; } } |