mirror of
https://github.com/postgres/postgres.git
synced 2025-08-27 07:42:10 +03:00
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
This commit is contained in:
@@ -1687,6 +1687,9 @@ fix_expr_common(PlannerInfo *root, Node *node)
|
||||
|
||||
if (!OidIsValid(saop->hashfuncid))
|
||||
record_plan_function_dependency(root, saop->hashfuncid);
|
||||
|
||||
if (!OidIsValid(saop->negfuncid))
|
||||
record_plan_function_dependency(root, saop->negfuncid);
|
||||
}
|
||||
else if (IsA(node, Const))
|
||||
{
|
||||
|
@@ -128,6 +128,7 @@ negate_clause(Node *node)
|
||||
newopexpr->opno = negator;
|
||||
newopexpr->opfuncid = InvalidOid;
|
||||
newopexpr->hashfuncid = InvalidOid;
|
||||
newopexpr->negfuncid = InvalidOid;
|
||||
newopexpr->useOr = !saopexpr->useOr;
|
||||
newopexpr->inputcollid = saopexpr->inputcollid;
|
||||
newopexpr->args = saopexpr->args;
|
||||
|
@@ -2140,27 +2140,71 @@ convert_saop_to_hashed_saop_walker(Node *node, void *context)
|
||||
Oid lefthashfunc;
|
||||
Oid righthashfunc;
|
||||
|
||||
if (saop->useOr && arrayarg && IsA(arrayarg, Const) &&
|
||||
!((Const *) arrayarg)->constisnull &&
|
||||
get_op_hash_functions(saop->opno, &lefthashfunc, &righthashfunc) &&
|
||||
lefthashfunc == righthashfunc)
|
||||
if (arrayarg && IsA(arrayarg, Const) &&
|
||||
!((Const *) arrayarg)->constisnull)
|
||||
{
|
||||
Datum arrdatum = ((Const *) arrayarg)->constvalue;
|
||||
ArrayType *arr = (ArrayType *) DatumGetPointer(arrdatum);
|
||||
int nitems;
|
||||
|
||||
/*
|
||||
* Only fill in the hash functions if the array looks large enough
|
||||
* for it to be worth hashing instead of doing a linear search.
|
||||
*/
|
||||
nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
|
||||
|
||||
if (nitems >= MIN_ARRAY_SIZE_FOR_HASHED_SAOP)
|
||||
if (saop->useOr)
|
||||
{
|
||||
/* Looks good. Fill in the hash functions */
|
||||
saop->hashfuncid = lefthashfunc;
|
||||
if (get_op_hash_functions(saop->opno, &lefthashfunc, &righthashfunc) &&
|
||||
lefthashfunc == righthashfunc)
|
||||
{
|
||||
Datum arrdatum = ((Const *) arrayarg)->constvalue;
|
||||
ArrayType *arr = (ArrayType *) DatumGetPointer(arrdatum);
|
||||
int nitems;
|
||||
|
||||
/*
|
||||
* Only fill in the hash functions if the array looks
|
||||
* large enough for it to be worth hashing instead of
|
||||
* doing a linear search.
|
||||
*/
|
||||
nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
|
||||
|
||||
if (nitems >= MIN_ARRAY_SIZE_FOR_HASHED_SAOP)
|
||||
{
|
||||
/* Looks good. Fill in the hash functions */
|
||||
saop->hashfuncid = lefthashfunc;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else /* !saop->useOr */
|
||||
{
|
||||
Oid negator = get_negator(saop->opno);
|
||||
|
||||
/*
|
||||
* Check if this is a NOT IN using an operator whose negator
|
||||
* is hashable. If so we can still build a hash table and
|
||||
* just ensure the lookup items are not in the hash table.
|
||||
*/
|
||||
if (OidIsValid(negator) &&
|
||||
get_op_hash_functions(negator, &lefthashfunc, &righthashfunc) &&
|
||||
lefthashfunc == righthashfunc)
|
||||
{
|
||||
Datum arrdatum = ((Const *) arrayarg)->constvalue;
|
||||
ArrayType *arr = (ArrayType *) DatumGetPointer(arrdatum);
|
||||
int nitems;
|
||||
|
||||
/*
|
||||
* Only fill in the hash functions if the array looks
|
||||
* large enough for it to be worth hashing instead of
|
||||
* doing a linear search.
|
||||
*/
|
||||
nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
|
||||
|
||||
if (nitems >= MIN_ARRAY_SIZE_FOR_HASHED_SAOP)
|
||||
{
|
||||
/* Looks good. Fill in the hash functions */
|
||||
saop->hashfuncid = lefthashfunc;
|
||||
|
||||
/*
|
||||
* Also set the negfuncid. The executor will need
|
||||
* that to perform hashtable lookups.
|
||||
*/
|
||||
saop->negfuncid = get_opcode(negator);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user