faster_partition_prune_v20_delta_drowley.patch
application/octet-stream
Filename: faster_partition_prune_v20_delta_drowley.patch
Type: application/octet-stream
Part: 0
Patch
Format: unified
Series: patch v20
| File | + | − |
|---|---|---|
| src/backend/catalog/partition.c | 182 | 114 |
| src/backend/optimizer/path/allpaths.c | 16 | 352 |
diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index 974febb..acd29eb 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -155,7 +155,6 @@ typedef struct PartitionRangeBound
*/
typedef struct PartitionBoundCmpArg
{
- bool is_bound;
union
{
PartitionListValue *lbound;
@@ -165,6 +164,7 @@ typedef struct PartitionBoundCmpArg
Datum *datums;
int ndatums;
+ bool is_bound;
} PartitionBoundCmpArg;
/*
@@ -177,7 +177,7 @@ typedef struct PartClause
Expr *constarg;
/* cached info. */
- bool valid_cache; /* Is the following information initialized? */
+ bool valid_cache; /* Are the following fields populated? */
int op_strategy;
Oid op_subtype;
FmgrInfo op_func;
@@ -199,45 +199,43 @@ typedef enum PartOpStrategy
* Information about partition look up keys to be passed to
* get_partitions_for_keys()
*
- * This information is extracted from the query's mutually conjunctive operator
- * clauses, each of whose variable argument is matched to a partition key and
- * operator is checked to be contained in the corresponding column's partition
- * operator family.
+ * Stores Datums and nullness properties found in clauses which match the
+ * partition key. Properties found are cache and are indexed by the partition
+ * key index.
*/
typedef struct PartScanKeyInfo
{
/*
- * Equality look up key. Values in the following array appear in no
- * particular order (unlike minkeys and maxkeys below which must appear in
- * the same order as the partition key columns). n_eqkeys must be equal to
- * the number of partition keys to be valid (except in the case of hash
- * partitioning where that's not required). When set, minkeys and maxkeys
- * are ignored.
+ * Equality look up key. Used to store known Datums values from clauses
+ * compared by an equality operation to the partition key.
*/
Datum eqkeys[PARTITION_MAX_KEYS];
- int n_eqkeys;
/*
- * Lower and upper bounds on a sequence of selected partitions. Values in
- * the following arrays must appear in the same order as the partition key
- * columns and may contain values for only a prefix of the partition key
- * columns. If *_incl is true then the corresponding bound is inclusive
- * and hence the partition into which the bound falls is to be included in
- * the set of selected partitions.
+ * Lower and upper bounds on a sequence of selected partitions. These may
+ * contain values for only a prefix of the partition keys.
*/
Datum minkeys[PARTITION_MAX_KEYS];
- int n_minkeys;
- bool min_incl;
-
Datum maxkeys[PARTITION_MAX_KEYS];
+
+ /*
+ * Number of values stored in the corresponding array above
+ */
+ int n_eqkeys;
+ int n_minkeys;
int n_maxkeys;
+
+ /*
+ * Properties to mark if the clauses found for the corresponding partition
+ * are inclusive of the stored value or not.
+ */
+ bool min_incl;
bool max_incl;
/*
- * Information about nullness of partition keys, either specified
+ * Information about nullness of the partition keys, either specified
* explicitly in the query (in the form of a IS [NOT] NULL clause) or
- * implied due to the assumption of strictness of the partitioning
- * operators.
+ * implied from strict clauses matching the partition key.
*/
Bitmapset *keyisnull;
Bitmapset *keyisnotnull;
@@ -293,8 +291,8 @@ PG_FUNCTION_INFO_V1(satisfies_hash_partition);
static Bitmapset *get_partitions_from_clauses_recurse(Relation relation,
int rt_index, List *clauses);
-static Bitmapset *get_partitions_excluded_by(Relation relation,
- List *ne_clauses);
+static Bitmapset *get_partitions_excluded_by_ne_clauses(Relation relation,
+ List *ne_clauses);
static Bitmapset *get_partitions_from_or_clause_args(Relation relation,
int rt_index, List *or_clause_args);
static bool classify_partition_bounding_keys(Relation relation, List *clauses,
@@ -1692,22 +1690,26 @@ get_partition_qual_relid(Oid relid)
/*
* get_partitions_from_clauses
- * Determine the set of partitions of 'relation' that will satisfy all
- * the clauses contained in 'partclauses'
+ * Determine all partitions of 'relation' that could possibly contain a
+ * record that matches 'partclauses'
*
- * Outputs:
- * A Bitmapset containing indexes of all selected partitions.
+ * Returns a Bitmapset of the matching partition indexes, or NULL if none can
+ * match.
*/
Bitmapset *
get_partitions_from_clauses(Relation relation, int rt_index,
List *partclauses)
{
- Bitmapset *result;
- List *partconstr;
+ List *clauses;
PartitionDesc partdesc = RelationGetPartitionDesc(relation);
PartitionBoundInfo boundinfo = partdesc->boundinfo;
- Assert(partclauses != NIL);
+ /* All partitions match if there are no clauses */
+ if (!partclauses)
+ return bms_add_range(NULL, 0, partdesc->nparts - 1);
+
+ /* Some functions called below modify this list */
+ clauses = list_copy(partclauses);
/*
* If relation is a partition itself, add its partition constraint
@@ -1719,19 +1721,17 @@ get_partitions_from_clauses(Relation relation, int rt_index,
* set of selected partitions for a query whose clauses select a key space
* bigger than the partition's.
*/
- if (partition_bound_has_default(boundinfo) &&
- (partconstr = RelationGetPartitionQual(relation)) != NIL)
+ if (partition_bound_has_default(boundinfo))
{
- partconstr = (List *) expression_planner((Expr *) partconstr);
+ List *partqual = RelationGetPartitionQual(relation);
- /* Be careful not to modify the input list. */
- partclauses = list_concat(list_copy(partclauses), partconstr);
- }
+ partqual = (List *) expression_planner((Expr *) partqual);
- result = get_partitions_from_clauses_recurse(relation, rt_index,
- partclauses);
+ clauses = list_concat(clauses, partqual);
+ }
- return result;
+ return get_partitions_from_clauses_recurse(relation, rt_index,
+ clauses);
}
/* Module-local functions */
@@ -1771,29 +1771,35 @@ get_partitions_from_clauses_recurse(Relation relation, int rt_index,
return NULL;
result = get_partitions_for_keys(relation, &keys);
+
+ /*
+ * No point in trying to look at other conjunctive clauses, if we got
+ * an empty set in the first place.
+ */
+ if (bms_is_empty(result))
+ return NULL;
}
else
{
+ /*
+ * We found nothing useful to indicate which partitions might need to
+ * be scanned. Perhaps we'll find something below that indicates
+ * which ones won't need to be scanned.
+ */
PartitionDesc partdesc = RelationGetPartitionDesc(relation);
result = bms_add_range(NULL, 0, partdesc->nparts - 1);
}
- /*
- * No point in trying to look at other conjunctive clauses, if we got
- * an empty set in the first place.
- */
- if (bms_is_empty(result))
- return NULL;
-
/* Select partitions by applying the clauses containing <> operators. */
if (ne_clauses)
{
Bitmapset *ne_clause_parts;
- ne_clause_parts = get_partitions_excluded_by(relation, ne_clauses);
+ ne_clause_parts = get_partitions_excluded_by_ne_clauses(relation,
+ ne_clauses);
- /* Remove any matched partitions */
+ /* Remove any partitions we found to not be needed */
result = bms_del_members(result, ne_clause_parts);
bms_free(ne_clause_parts);
}
@@ -1820,14 +1826,14 @@ get_partitions_from_clauses_recurse(Relation relation, int rt_index,
}
/*
- * get_partitions_excluded_by
+ * get_partitions_excluded_by_ne_clauses
*
* Returns a Bitmapset of partition indexes of any partition that can safely
* be removed due to 'ne_clauses' containing not-equal clauses for all
* possible values that the partition can contain.
*/
static Bitmapset *
-get_partitions_excluded_by(Relation relation, List *ne_clauses)
+get_partitions_excluded_by_ne_clauses(Relation relation, List *ne_clauses)
{
ListCell *lc;
Bitmapset *excluded_parts = NULL;
@@ -1979,45 +1985,47 @@ get_partitions_from_or_clause_args(Relation relation, int rt_index,
/* Match partition key (partattno/partexpr) to an expression (expr). */
#define EXPR_MATCHES_PARTKEY(expr, partattno, partexpr) \
- ((partattno) != 0 ?\
- (IsA((expr), Var) &&\
- ((Var *) (expr))->varattno == (partattno)) :\
+ ((partattno) != 0 ? \
+ (IsA((expr), Var) && \
+ ((Var *) (expr))->varattno == (partattno)) : \
equal((expr), (partexpr)))
+#define COLLATION_MATCH(partcoll, exprcoll) \
+ (!OidIsValid(partcoll) || (partcoll) == (exprcoll))
+
/*
* classify_partition_bounding_keys
- * Analyzes partition clauses to collect the equality key or minimum and
- * maximum bounding keys using which to look up partitions of relation.
- * Also collects information about the nullness of the individual
- * partition key columns as the partitions may have certain properties
- * with respect to null values. Keys and nullness information are stored
- * in the output argument *keys.
+ * Analyzes 'clauses' clauses to collect the equality key, minimum,
+ * maximum bounding keys. We also collect nullability properties along
+ * the way. All these are then used to determine the minimum set of
+ * partitions that need to be scanned to find all records matching
+ * the clause list.
*
- * Clauses in the provided list are assumed to be implicitly ANDed, each of
- * which is known to match some partition key column. They're mapped to the
- * individual key columns and for each column, we find constant values that
- * are compared to the column using operators that are compatible with
- * partitioning. For example, if there is a clause a = 4 where a is a
- * partition key column, then 4 is stored as the equality key if = is
- * partitioning equality operator. If there are clauses a > 1 and a < 5, then
- * 1 and 5 are stored as the minimum and maximum bounding keys, if > and < are
- * partitioning less and greater operators, respectively. If there are
- * multiple clauses addressing a given column, we first try to check if they
- * are mutually contradictory and set *constfalse if so. For example, if there
- * are clauses a = 1 and a = 2 in the list, then clearly both will never be
- * true. Similarly for a > 1 and a < 0. For clauses containing ordering
+ * Clauses found to match a partition are are analyzed to determine if the
+ * clause is useful for partition elimination. For this to work the value
+ * being compared to the partition key must be a known value, e.g. a Const.
+ * We attempt to determine the narrowest range of values that will match by
+ * collecting and storing values that further narrow the range of the possible
+ * partitions to scan. For example, if x is a partition key and we see a
+ * clause such as x = 4, 4 is stored as the equality key if = is partitioning
+ * equality operator. If there are clauses x > 1 and x < 5, then 1 and 5 are
+ * stored as the minimum and maximum bounding keys, respectively, providing
+ * that > and < are partitioning less and greater operators, If there are
+ * multiple clauses matching a given column, we first try to check if they are
+ * mutually contradictory and, if so set *constfalse to true. For example, if
+ * there are clauses x = 1 and x = 2 in the list, then clearly both will never
+ * be true. Similarly for x > 1 and x < 0. For clauses containing ordering
* operators that are non-contradictory, we try to find the one that is the
- * most restrictive and discard others. For example, of a > 1, a > 2, and
- * a >= 5, the last one is the most restrictive and so 5 is the best minimum
+ * most restrictive and discard others. For example, of x > 1, x > 2, and
+ * x >= 5, the last one is the most restrictive and so 5 is the best minimum
* bound (which also happens to be inclusive), so it is kept while discarding
- * both a > 1 and a > 2.
+ * both x > 1 and x > 2.
*
- * For multi-column keys, an equality key needs to contain values corresponding
- * to *all* partition key columns in the range patitioning case, whereas it's
- * not necessary for hash partitioning. Actually, the latter requires that
- * the remaining columns are covered by IS NULL clauses, but that's not checked
- * in this function. Minimum and maximum bound keys are allowed to contain
- * values for only a prefix partition key columns.
+ * For RANGE partitioning we do not need to match all partition keys. We may
+ * be able to eliminate some partitions with just a prefix of the partition
+ * keys. HASH partitioning does require all keys are matched to with at least
+ * some combinations of equality clauses and IS NULL clauses. LIST partitions
+ * don't support multiple partition keys.
*
* Certain kinds of clauses are not immediately handled within this function
* and are instead returned to the caller for further processing. That
@@ -2025,10 +2033,10 @@ get_partitions_from_or_clause_args(Relation relation, int rt_index,
* generated from ScalarArrayOpExpr clauses in the input list that have useOr
* set to true), which are returned to the caller in *or_clauses and clauses
* containing a <> operator (whose negator is a valid *list* partitioning
- * equality operator), which are returned to the caller to in *ne_clauses.
+ * equality operator), which are returned to the caller via *ne_clauses.
*
- * True is returned if *keys contains valid information upon return or if
- * *constfalse is set to true.
+ * True is returned if *keys contains use information and also if *constfalse
+ * has been set to true.
*/
static bool
classify_partition_bounding_keys(Relation relation, List *clauses,
@@ -2048,7 +2056,7 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
bool need_next_eq,
need_next_min,
need_next_max;
- int n_keynullness = 0;
+ bool got_nullcheck = false;
*or_clauses = NIL;
*ne_clauses = NIL;
@@ -2097,22 +2105,21 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
{
Oid partopfamily = partkey->partopfamily[i];
AttrNumber partattno = partkey->partattrs[i];
+ Oid partcoll = partkey->partcollation[i];
Expr *partexpr = NULL;
PartClause *pc;
+ Oid commutator = InvalidOid;
/*
- * A non-zero partattno refers to a simple column reference that
- * will be matched against varattno of a Var appearing the clause.
- * partattno == 0 refers to arbitrary expressions, which get the
- * current one from PartitionKey.
+ * A zero attno means the partition key is an expression, so grab
+ * the next expression from the list.
*/
if (partattno == 0)
{
if (partexprs_item == NULL)
elog(ERROR, "wrong number of partition key expressions");
- /* Copy to avoid overwriting the relcache's content. */
- partexpr = copyObject(lfirst(partexprs_item));
+ partexpr = (Expr *) lfirst(partexprs_item);
/*
* Expressions stored in PartitionKey in the relcache all
@@ -2121,7 +2128,11 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
* correctly matched to the expressions coming from the query.
*/
if (rt_index != 1)
+ {
+ /* make a copy so as now to overwrite the relcache */
+ partexpr = (Expr *) copyObject(partexpr);
ChangeVarNodes((Node *) partexpr, 1, rt_index, 0);
+ }
partexprs_item = lnext(partexprs_item);
}
@@ -2140,15 +2151,43 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
rightop = (Expr *) get_rightop(clause);
if (IsA(rightop, RelabelType))
rightop = ((RelabelType *) rightop)->arg;
+
+ /* check if the clause matches the partition key */
if (EXPR_MATCHES_PARTKEY(leftop, partattno, partexpr))
constexpr = rightop;
else if (EXPR_MATCHES_PARTKEY(rightop, partattno, partexpr))
+ {
constexpr = leftop;
+
+ commutator = get_commutator(opclause->opno);
+
+ /* nothing we can do unless we can swap the operands */
+ if (!OidIsValid(commutator))
+ continue;
+ }
else
/* Clause does not match this partition key. */
continue;
/*
+ * Also, useless, if the clause's collation is different from
+ * the partitioning collation.
+ */
+ if (!COLLATION_MATCH(partcoll, opclause->inputcollid))
+ continue;
+
+ /*
+ * Only allow strict operators. This will guarantee nulls are
+ * filtered.
+ */
+ if (!op_strict(opclause->opno))
+ continue;
+
+ /* Useless if the "constant" can change its value. */
+ if (contain_volatile_functions((Node *) constexpr))
+ continue;
+
+ /*
* Handle cases where the clause's operator does not belong to
* the partitioning operator family. We currently handle two
* such cases: 1. Operators named '<>' are not listed in any
@@ -2190,30 +2229,23 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
continue;
}
- pc = palloc0(sizeof(PartClause));
+ pc = (PartClause *) palloc0(sizeof(PartClause));
pc->constarg = constexpr;
/*
- * Flip the left and right args if we have to, because the
- * code which extract the constant value to use for
- * partition-pruning expects to find it as the rightop of the
- * clause. (See below in this function.)
+ * If commutator is set to a valid Oid then we'll need to swap
+ * the left and right operands. Later code requires that the
+ * partkey is on the left side.
*/
- if (constexpr == rightop)
+ if (!OidIsValid(commutator))
pc->op = opclause;
else
{
OpExpr *commuted;
- Oid commutator = get_commutator(opclause->opno);
- /*
- * Caller must have made sure to check that the commutator
- * indeed exists.
- */
- Assert(OidIsValid(commutator));
commuted = (OpExpr *) copyObject(opclause);
commuted->opno = commutator;
- commuted->opfuncid = get_opcode(commuted->opno);
+ commuted->opfuncid = get_opcode(commutator);
commuted->args = list_make2(rightop, leftop);
pc->op = commuted;
}
@@ -2221,7 +2253,7 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
/*
* We don't turn a <> operator clause into a key right away.
* Instead, the caller will hand over such clauses to
- * get_partitions_excluded_by().
+ * get_partitions_excluded_by_ne_clauses().
*/
if (is_ne_listp)
*ne_clauses = lappend(*ne_clauses, pc);
@@ -2255,13 +2287,32 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
ListCell *lc1;
bool negated = false;
- /* Clause does not match this partition key. */
if (IsA(leftop, RelabelType))
leftop = ((RelabelType *) leftop)->arg;
+
+ /* Check it matches this partition key */
if (!EXPR_MATCHES_PARTKEY(leftop, partattno, partexpr))
continue;
/*
+ * Also, useless, if the clause's collation is different from
+ * the partitioning collation.
+ */
+ if (!COLLATION_MATCH(partcoll, saop->inputcollid))
+ continue;
+
+ /*
+ * Only allow strict operators. This will guarantee null are
+ * filtered.
+ */
+ if (!op_strict(saop->opno))
+ continue;
+
+ /* Useless if the array has any volatile functions. */
+ if (contain_volatile_functions((Node *) rightop))
+ continue;
+
+ /*
* In case of NOT IN (..), we get a '<>', which while not
* listed as part of any operator family, we are able to
* handle it if its negator is indeed a part of the
@@ -2343,10 +2394,10 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
elem_clauses = NIL;
foreach(lc1, elem_exprs)
{
- Const *rightop = castNode(Const, lfirst(lc1));
+ Expr *rightop = (Expr *) lfirst(lc1);
Expr *elem_clause;
- if (rightop->constisnull)
+ if (IsA(rightop, Const) && ((Const *) rightop)->constisnull)
{
NullTest *nulltest = makeNode(NullTest);
@@ -2399,6 +2450,7 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
{
if (nulltest->nulltesttype == IS_NULL)
{
+ /* check for conflicting IS NOT NULLs */
if (bms_is_member(i, keyisnotnull))
{
*constfalse = true;
@@ -2407,8 +2459,17 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
keyisnull = bms_add_member(keyisnull, i);
}
else
+ {
+ /* check for conflicting IS NULLs */
+ if (bms_is_member(i, keyisnull))
+ {
+ *constfalse = true;
+ return true;
+ }
+
keyisnotnull = bms_add_member(keyisnotnull, i);
- n_keynullness++;
+ }
+ got_nullcheck = true;
will_compute_keys = true;
}
}
@@ -2431,9 +2492,15 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
{
BooleanTest *btest = (BooleanTest *) clause;
+ /* Only IS [NOT] TRUE/FALSE are any good to us */
+ if (btest->booltesttype == IS_UNKNOWN ||
+ btest->booltesttype == IS_NOT_UNKNOWN)
+ continue;
+
leftop = btest->arg;
if (IsA(leftop, RelabelType))
leftop = ((RelabelType *) leftop)->arg;
+
/* Clause does not match this partition key. */
if (!EXPR_MATCHES_PARTKEY(leftop, partattno, partexpr))
continue;
@@ -2450,6 +2517,7 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
: (Expr *) get_notclausearg((Expr *) clause);
if (IsA(leftop, RelabelType))
leftop = ((RelabelType *) leftop)->arg;
+
/* Clause does not match this partition key. */
if (!EXPR_MATCHES_PARTKEY(leftop, partattno, partexpr))
continue;
@@ -2616,7 +2684,7 @@ classify_partition_bounding_keys(Relation relation, List *clauses,
keys->keyisnotnull = keyisnotnull;
return (keys->n_eqkeys > 0 || keys->n_minkeys > 0 ||
- keys->n_maxkeys > 0 || n_keynullness > 0);
+ keys->n_maxkeys > 0 || got_nullcheck);
}
/*
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index e7c7a6e..51648c8 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -858,54 +858,24 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/*
* get_append_rel_partitions
- * Return the list of partitions of rel that pass the clauses mentioned
- * in rel->baserestrictinfo. An empty list is returned if no matching
- * partitions were found.
- *
- * Returned list contains the AppendRelInfos of chosen partitions.
+ * Returns a List of AppendRelInfo belonging to the minimum set of
+ * partitions which must be scanned to satisfy rel's baserestrictinfo
+ * quals.
*/
static List *
get_append_rel_partitions(PlannerInfo *root,
RelOptInfo *rel,
RangeTblEntry *rte)
{
- List *partclauses;
- bool contains_const,
- constfalse;
- List *result = NIL;
- int i;
- Relation parent;
- PartitionDesc partdesc;
+ Relation partrel;
Bitmapset *partindexes;
+ List *result = NIL;
+ int i;
- /*
- * Get the clauses that match the partition key. It's also a good idea
- * to check if the matched clauses contain constant values that can be
- * used for pruning and go to get_partitions_from_clauses() only if so.
- * If rel->baserestrictinfo might contain mutually contradictory clauses,
- * also find out about that.
- */
- partclauses = match_clauses_to_partkey(root, rel, rel->baserestrictinfo,
- &contains_const, &constfalse);
+ partrel = heap_open(rte->relid, NoLock);
- /* We're done here. */
- if (constfalse)
- return NIL;
-
- parent = heap_open(rte->relid, NoLock);
- partdesc = RelationGetPartitionDesc(parent);
-
- if (partclauses != NIL && contains_const)
- partindexes = get_partitions_from_clauses(parent, rel->relid,
- partclauses);
- else
- {
- /*
- * There are no clauses that are useful to prune any partitions, so
- * scan all partitions.
- */
- partindexes = bms_add_range(NULL, 0, partdesc->nparts - 1);
- }
+ partindexes = get_partitions_from_clauses(partrel, rel->relid,
+ rel->baserestrictinfo);
/* Fetch the partition appinfos. */
i = -1;
@@ -914,328 +884,22 @@ get_append_rel_partitions(PlannerInfo *root,
AppendRelInfo *appinfo = rel->part_appinfos[i];
#ifdef USE_ASSERT_CHECKING
- RangeTblEntry *rte = planner_rt_fetch(appinfo->child_relid, root);
+ PartitionDesc partdesc = RelationGetPartitionDesc(partrel);
+ RangeTblEntry *childrte;
+
+ childrte = planner_rt_fetch(appinfo->child_relid, root);
/*
* Must be the intended child's RTE here, because appinfos are ordered
* the same way as partitions in the partition descriptor.
*/
- Assert(partdesc->oids[i] == rte->relid);
+ Assert(partdesc->oids[i] == childrte->relid);
#endif
+
result = lappend(result, appinfo);
}
- heap_close(parent, NoLock);
-
- return result;
-}
-
-#define PartCollMatchesExprColl(partcoll, exprcoll) \
- ((partcoll) == InvalidOid || (partcoll) == (exprcoll))
-
-/*
- * match_clauses_to_partkey
- * Match clauses with rel's partition key
- *
- * Returned list contains clauses matched to the partition key columns and
- * *contains_const and *constfalse are set as described below.
- *
- * For an individual clause to match with a partition key column, the clause
- * must be an operator clause of the form (partkey op const) or (const op
- * partkey); the latter only if a suitable commutator exists. Furthermore,
- * the operator must be strict and its input collation must match the partition
- * collation. The aforementioned "const" means any expression that doesn't
- * involve a volatile function or a Var of this relation. We allow Vars
- * belonging to other relations (for example, if the clause is a join clause),
- * but they are treated as parameters whose values are not known now, so cannot
- * be used for partition pruning right within the planner. It's the
- * responsibility of higher code levels to manage restriction and join clauses
- * appropriately. If a NullTest against a partition key is encountered, it's
- * added to the result as well.
- *
- * *contains_const is set if at least one matched clauses contains the constant
- * operand or is a Nullness test. *constfalse is set if the input list
- * contains a pseudo-constant RestrictInfo with false value.
- */
-static List *
-match_clauses_to_partkey(PlannerInfo *root,
- RelOptInfo *rel,
- List *clauses,
- bool *contains_const,
- bool *constfalse)
-{
- PartitionScheme partscheme = rel->part_scheme;
- List *result = NIL;
- ListCell *lc;
-
- *contains_const = false;
- *constfalse = false;
-
- Assert (partscheme != NULL);
-
- /* Make a copy, because we may scribble on it below. */
- clauses = list_copy(clauses);
-
- foreach(lc, clauses)
- {
- Node *member = lfirst(lc);
- Expr *clause;
- int i;
-
- if (IsA(member, RestrictInfo))
- {
- RestrictInfo *rinfo = (RestrictInfo *) member;
-
- clause = rinfo->clause;
- if (rinfo->pseudoconstant &&
- (IsA(clause, Const) &&
- ((((Const *) clause)->constisnull) ||
- !DatumGetBool(((Const *) clause)->constvalue))))
- {
- *constfalse = true;
- return NIL;
- }
- }
- else
- clause = (Expr *) member;
-
- /*
- * For a BoolExpr, we should try to match each of its args with the
- * partition key as described below for each type.
- */
- if (IsA(clause, BoolExpr))
- {
- if (or_clause((Node *) clause))
- {
- /*
- * For each of OR clause's args, call this function
- * recursively with a given arg as the only member in the
- * input list and see if it's returned as matching the
- * partition key. Add the OR clause to the result iff at
- * least one of its args contain a matching clause.
- */
- BoolExpr *orclause = (BoolExpr *) clause;
- ListCell *lc1;
- bool arg_matches_key = false,
- matched_arg_contains_const = false,
- all_args_constfalse = true;
-
- foreach (lc1, orclause->args)
- {
- Node *arg = lfirst(lc1);
- bool contains_const1,
- constfalse1;
-
- if (match_clauses_to_partkey(root, rel, list_make1(arg),
- &contains_const1,
- &constfalse1) != NIL)
- {
- arg_matches_key = true;
- matched_arg_contains_const = contains_const1;
- }
-
- /* We got at least one arg that is not constant false. */
- if (!constfalse1)
- all_args_constfalse = false;
- }
-
- if (arg_matches_key)
- {
- result = lappend(result, clause);
- *contains_const = matched_arg_contains_const;
- }
-
- /* OR clause is "constant false" if all of its args are. */
- *constfalse = all_args_constfalse;
- continue;
- }
- else if (and_clause((Node *) clause))
- {
- /*
- * Since the clause is itself implicitly ANDed with other
- * clauses in the input list, queue the args to be processed
- * later as if they were part of the original input list.
- */
- clauses = list_concat(clauses,
- list_copy(((BoolExpr *) clause)->args));
- continue;
- }
-
- /* Fall-through for a NOT clause, which is handled below. */
- }
-
- for (i = 0; i < partscheme->partnatts; i++)
- {
- Node *partkey = linitial(rel->partexprs[i]);
- Oid partopfamily = partscheme->partopfamily[i],
- partcoll = partscheme->partcollation[i];
-
- /*
- * Check if the clauses matches the partition key and add it to
- * the result list if other things such as operator input
- * collation, strictness, etc. look fine.
- */
- if (is_opclause(clause))
- {
- Expr *constexpr,
- *leftop,
- *rightop;
- Relids constrelids;
- Oid expr_op,
- expr_coll;
-
- leftop = (Expr *) get_leftop(clause);
- rightop = (Expr *) get_rightop(clause);
- expr_op = ((OpExpr *) clause)->opno;
- expr_coll = ((OpExpr *) clause)->inputcollid;
-
- if (IsA(leftop, RelabelType))
- leftop = ((RelabelType *) leftop)->arg;
- if (IsA(rightop, RelabelType))
- rightop = ((RelabelType *) rightop)->arg;
-
- if (equal(leftop, partkey))
- {
- constexpr = rightop;
- constrelids = pull_varnos((Node *) rightop);
- }
- else if (equal(rightop, partkey))
- {
- constexpr = leftop;
- constrelids = pull_varnos((Node *) leftop);
- expr_op = get_commutator(expr_op);
-
- /*
- * If no commutator exists, cannot flip the qual's args,
- * so give up.
- */
- if (!OidIsValid(expr_op))
- continue;
- }
- else
- /* Neither argument matches the partition key. */
- continue;
-
- /*
- * Useless if what we're thinking of as a constant is actually
- * a Var coming from this relation.
- */
- if (bms_is_member(rel->relid, constrelids))
- continue;
-
- /*
- * Also, useless, if the clause's collation is different from
- * the partitioning collation.
- */
- if (!PartCollMatchesExprColl(partcoll, expr_coll))
- continue;
-
- /*
- * Only allow strict operators to think sanely about the
- * behavior with null arguments.
- */
- if (!op_strict(expr_op))
- continue;
-
- /* Useless if the "constant" can change its value. */
- if (contain_volatile_functions((Node *) constexpr))
- continue;
-
- /*
- * Everything seems to be fine, so add it to the list of
- * clauses we will use for pruning.
- */
- result = lappend(result, clause);
-
- if (!*contains_const)
- *contains_const = IsA(constexpr, Const);
- }
- else if (IsA(clause, ScalarArrayOpExpr))
- {
- ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
- Oid saop_op = saop->opno;
- Oid saop_coll = saop->inputcollid;
- Node *leftop = (Node *) linitial(saop->args),
- *rightop = (Node *) lsecond(saop->args);
-
- if (IsA(leftop, RelabelType))
- leftop = (Node *) ((RelabelType *) leftop)->arg;
- if (!equal(leftop, partkey))
- continue;
-
- /* Check if saop_op is compatible with partitioning. */
- if (!op_strict(saop_op))
- continue;
-
- /* Useless if the "constant" can change its value. */
- if (contain_volatile_functions((Node *) rightop))
- continue;
-
- /*
- * Also, useless, if the clause's collation is different from
- * the partitioning collation.
- */
- if (!PartCollMatchesExprColl(partcoll, saop_coll))
- continue;
-
- /* OK to add to the result. */
- result = lappend(result, clause);
- if (IsA(eval_const_expressions(root, rightop), Const))
- *contains_const = true;
- else
- *contains_const = false;
- }
- else if (IsA(clause, NullTest))
- {
- NullTest *nulltest = (NullTest *) clause;
- Node *arg = (Node *) nulltest->arg;
-
- if (equal(arg, partkey))
- {
- result = lappend(result, nulltest);
- /* A Nullness test can be used right away. */
- *contains_const = true;
- }
- }
- /*
- * Certain Boolean conditions have a special shape, which we
- * accept if the partitioning opfamily accepts Boolean conditions.
- */
- else if (IsBooleanOpfamily(partopfamily) &&
- (IsA(clause, BooleanTest) ||
- IsA(clause, Var) || not_clause((Node *) clause)))
- {
- /*
- * Only accept those for pruning that appear to be
- * IS [NOT] TRUE/FALSE.
- */
- if (IsA(clause, BooleanTest))
- {
- BooleanTest *btest = (BooleanTest *) clause;
- Expr *arg = btest->arg;
-
- if (btest->booltesttype != IS_UNKNOWN &&
- btest->booltesttype != IS_NOT_UNKNOWN &&
- equal((Node *) arg, partkey))
- result = lappend(result, clause);
- }
- else if (IsA(clause, Var))
- {
- if (equal((Node *) clause, partkey))
- result = lappend(result, clause);
- }
- else
- {
- Node *arg = (Node *) get_notclausearg((Expr *) clause);
-
- if (equal(arg, partkey))
- result = lappend(result, clause);
- }
-
- *contains_const = true;
- }
- }
- }
+ heap_close(partrel, NoLock);
return result;
}