0002-OR-to-ANY-in-index.diff
text/x-patch
Filename: 0002-OR-to-ANY-in-index.diff
Type: text/x-patch
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch 0002
| File | + | − |
|---|---|---|
| src/backend/optimizer/path/allpaths.c | 429 | 0 |
| src/backend/optimizer/path/indxpath.c | 0 | 394 |
| src/backend/optimizer/util/relnode.c | 91 | 0 |
| src/include/optimizer/pathnode.h | 2 | 0 |
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 67921a08262..e0d83672756 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -50,6 +50,12 @@
#include "port/pg_bitutils.h"
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
+#include "parser/parse_coerce.h"
+#include "common/hashfn.h"
+#include "catalog/pg_operator.h"
+#include "nodes/queryjumble.h"
+#include "utils/syscache.h"
+#include "catalog/namespace.h"
/* Bitmask flags for pushdown_safety_info.unsafeFlags */
@@ -165,6 +171,390 @@ static void remove_unused_subquery_outputs(Query *subquery, RelOptInfo *rel,
Bitmapset *extra_used_attrs);
+bool enable_or_transformation = true;
+typedef struct OrClauseGroupKey
+{
+ Expr *expr; /* Pointer to the expression tree which has been a source for
+ the hashkey value */
+ Oid opno;
+ Oid exprtype;
+} OrClauseGroupKey;
+
+typedef struct OrClauseGroupEntry
+{
+ OrClauseGroupKey key;
+ Node *node;
+ List *consts;
+ Oid collation;
+ RestrictInfo *rinfo;
+ List *exprs;
+} OrClauseGroupEntry;
+
+/*
+ * Hash function that's compatible with guc_name_compare
+ */
+static uint32
+orclause_hash(const void *data, Size keysize)
+{
+ OrClauseGroupKey *key = (OrClauseGroupKey *) data;
+ uint64 hash;
+
+ (void) JumbleExpr(key->expr, &hash);
+ hash = ((uint64) key->opno + (uint64) key->exprtype) % UINT64_MAX;
+ return hash;
+}
+
+static void *
+orclause_keycopy(void *dest, const void *src, Size keysize)
+{
+ OrClauseGroupKey *src_key = (OrClauseGroupKey *) src;
+ OrClauseGroupKey *dst_key = (OrClauseGroupKey *) dest;
+
+ Assert(sizeof(OrClauseGroupKey) == keysize);
+
+ dst_key->expr = src_key->expr;
+ dst_key->opno = src_key->opno;
+ dst_key->exprtype = src_key->exprtype;
+ return dst_key;
+}
+
+/*
+ * Dynahash match function to use in guc_hashtab
+ */
+static int
+orclause_match(const void *data1, const void *data2, Size keysize)
+{
+ OrClauseGroupKey *key1 = (OrClauseGroupKey *) data1;
+ OrClauseGroupKey *key2 = (OrClauseGroupKey *) data2;
+
+ Assert(sizeof(OrClauseGroupKey) == keysize);
+
+ if (key1->opno == key2->opno && key1->exprtype == key2->exprtype &&
+ equal(key1->expr, key2->expr))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Pass through baserestrictinfo clauses and try to convert OR clauses into IN
+ * Return a modified clause list or just the same baserestrictinfo, if no
+ * changes have made.
+ * XXX: do not change source list of clauses at all.
+ */
+static List *
+transform_ors(PlannerInfo *root, List *baserestrictinfo, bool *something_changed)
+{
+ ListCell *lc;
+ List *modified_rinfo = NIL;
+
+ /*
+ * Complexity of a clause could be arbitrarily sophisticated. Here, we will
+ * look up only on the top level of clause list.
+ * XXX: It is substantiated? Could we change something here?
+ */
+ foreach (lc, baserestrictinfo)
+ {
+ RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
+ RestrictInfo *rinfo_base = copyObject(rinfo);
+ List *or_list = NIL;
+ ListCell *lc_eargs,
+ *lc_rargs;
+ bool change_apply = false;
+ HASHCTL info;
+ HTAB *or_group_htab = NULL;
+ int len_ors;
+ HASH_SEQ_STATUS hash_seq;
+ OrClauseGroupEntry *gentry;
+
+ if (!enable_or_transformation || !restriction_is_or_clause(rinfo))
+ {
+ /* Add a clause without changes */
+ modified_rinfo = lappend(modified_rinfo, rinfo);
+ continue;
+ }
+
+ len_ors = ((BoolExpr *) rinfo->clause)->args? list_length(((BoolExpr *) rinfo->clause)->args) : 0;
+
+ if (len_ors < 2)
+ {
+ /* Add a clause without changes */
+ modified_rinfo = lappend(modified_rinfo, rinfo);
+ continue;
+ }
+
+ MemSet(&info, 0, sizeof(info));
+ info.keysize = sizeof(OrClauseGroupKey);
+ info.entrysize = sizeof(OrClauseGroupEntry);
+ info.hash = orclause_hash;
+ info.keycopy = orclause_keycopy;
+ info.match = orclause_match;
+ or_group_htab = hash_create("OR Groups",
+ len_ors,
+ &info,
+ HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_KEYCOPY);
+
+ /*
+ * NOTE:
+ * It is an OR-clause. So, rinfo->orclause is a BoolExpr node, contains
+ * a list of sub-restrictinfo args, and rinfo->clause - which is the
+ * same expression, made from bare clauses. To not break selectivity
+ * caches and other optimizations, use both:
+ * - use rinfos from orclause if no transformation needed
+ * - use bare quals from rinfo->clause in the case of transformation,
+ * to create new RestrictInfo: in this case we have no options to avoid
+ * selectivity estimation procedure.
+ */
+ forboth(lc_eargs, ((BoolExpr *) rinfo->clause)->args,
+ lc_rargs, ((BoolExpr *) rinfo->orclause)->args)
+ {
+ Expr *or_qual = (Expr *) lfirst(lc_eargs);
+ RestrictInfo *sub_rinfo;
+ Node *const_expr;
+ Node *nconst_expr;
+ OrClauseGroupKey hashkey;
+ bool found = false;
+
+ /* It may be one more boolean expression, skip it for now */
+ if (!IsA(lfirst(lc_rargs), RestrictInfo))
+ {
+ or_list = lappend(or_list, (void *) or_qual);
+ continue;
+ }
+
+ sub_rinfo = lfirst_node(RestrictInfo, lc_rargs);
+
+ /* Check: it is an expr of the form 'F(x) oper ConstExpr' */
+ if (!IsA(or_qual, OpExpr) ||
+ !(bms_is_empty(sub_rinfo->left_relids) ^
+ bms_is_empty(sub_rinfo->right_relids)) ||
+ contain_volatile_functions((Node *) or_qual))
+ {
+ /* Again, it's not the expr we can transform */
+ or_list = lappend(or_list, (void *) or_qual);
+ continue;
+ }
+
+ /* Get pointers to constant and expression sides of the clause */
+ const_expr =bms_is_empty(sub_rinfo->left_relids) ?
+ get_leftop(sub_rinfo->clause) :
+ get_rightop(sub_rinfo->clause);
+ nconst_expr = bms_is_empty(sub_rinfo->left_relids) ?
+ get_rightop(sub_rinfo->clause) :
+ get_leftop(sub_rinfo->clause);
+
+ if (!op_mergejoinable(((OpExpr *) sub_rinfo->clause)->opno, exprType(nconst_expr)))
+ {
+ /* And again, filter out non-equality operators */
+ or_list = lappend(or_list, (void *) or_qual);
+ continue;
+ }
+
+ /*
+ * At this point we definitely have a transformable clause.
+ * Classify it and add into specific group of clauses, or create new
+ * group.
+ */
+ hashkey.expr = (Expr *) nconst_expr;
+ hashkey.opno = ((OpExpr *) or_qual)->opno;
+ hashkey.exprtype = exprType(nconst_expr);
+ gentry = hash_search(or_group_htab, &hashkey, HASH_ENTER, &found);
+
+ if (unlikely(found))
+ {
+ gentry->consts = lappend(gentry->consts, const_expr);
+ gentry->exprs = lappend(gentry->exprs, or_qual);
+ }
+ else
+ {
+ gentry->node = nconst_expr;
+ gentry->consts = list_make1(const_expr);
+ gentry->exprs = list_make1(or_qual);
+ gentry->rinfo = sub_rinfo;
+ gentry->collation = exprInputCollation((Node *)sub_rinfo->clause);
+ }
+ }
+
+ if (list_length(or_list) == len_ors)
+ {
+ /*
+ * No any transformations possible with this list of arguments. Here we
+ * already made all underlying transformations. Thus, just return the
+ * transformed bool expression.
+ */
+ modified_rinfo = lappend(modified_rinfo, rinfo);
+ continue;
+ }
+
+ if (!or_group_htab && hash_get_num_entries(or_group_htab) < 1)
+ {
+ /*
+ * No any transformations possible with this rinfo, just add itself
+ * to the list and go further.
+ */
+ modified_rinfo = lappend(modified_rinfo, rinfo);
+ continue;
+ }
+
+ hash_seq_init(&hash_seq, or_group_htab);
+
+ /* Let's convert each group of clauses to an IN operation. */
+
+ /*
+ * Go through the list of groups and convert each, where number of
+ * consts more than 1. trivial groups move to OR-list again
+ */
+
+ while ((gentry = (OrClauseGroupEntry *) hash_seq_search(&hash_seq)) != NULL)
+ {
+ Oid scalar_type;
+ Oid array_type;
+
+ Assert(list_length(gentry->consts) > 0);
+ Assert(list_length(gentry->exprs) == list_length(gentry->consts));
+
+ if (list_length(gentry->consts) == 1)
+ {
+ /*
+ * Only one element in the class. Return rinfo into the BoolExpr
+ * args list unchanged.
+ */
+ list_free(gentry->consts);
+ or_list = list_concat(or_list, gentry->exprs);
+ continue;
+ }
+
+ /*
+ * Do the transformation.
+ *
+ * First of all, try to select a common type for the array elements.
+ * Note that since the LHS' type is first in the list, it will be
+ * preferred when there is doubt (eg, when all the RHS items are
+ * unknown literals).
+ *
+ * Note: use list_concat here not lcons, to avoid damaging rnonvars.
+ *
+ * As a source of insides, use make_scalar_array_op()
+ */
+ scalar_type = gentry->key.exprtype;
+
+ if (scalar_type != RECORDOID && OidIsValid(scalar_type))
+ array_type = get_array_type(scalar_type);
+ else
+ array_type = InvalidOid;
+
+ if (array_type != InvalidOid && scalar_type != InvalidOid)
+ {
+ /*
+ * OK: coerce all the right-hand non-Var inputs to the common
+ * type and build an ArrayExpr for them.
+ */
+ List *aexprs = NULL;
+ ArrayExpr *newa = NULL;
+ ScalarArrayOpExpr *saopexpr = NULL;
+ HeapTuple opertup;
+ Form_pg_operator operform;
+ List *namelist = NIL;
+
+ aexprs = gentry->consts;
+
+ newa = makeNode(ArrayExpr);
+ /* array_collid will be set by parse_collate.c */
+ newa->element_typeid = scalar_type;
+ newa->array_typeid = array_type;
+ newa->multidims = false;
+ newa->elements = aexprs;
+ newa->location = -1;
+
+ opertup = SearchSysCache1(OPEROID,
+ ObjectIdGetDatum(gentry->key.opno));
+
+ if (!HeapTupleIsValid(opertup))
+ elog(ERROR, "cache lookup failed for operator %u",
+ gentry->key.opno);
+
+ operform = (Form_pg_operator) GETSTRUCT(opertup);
+ if (!OperatorIsVisible(gentry->key.opno))
+ namelist = lappend(namelist, makeString(get_namespace_name(operform->oprnamespace)));
+
+ namelist = lappend(namelist, makeString(pstrdup(NameStr(operform->oprname))));
+ ReleaseSysCache(opertup);
+
+ saopexpr = makeNode(ScalarArrayOpExpr);
+ saopexpr->opno = ((OpExpr *) gentry->rinfo->clause)->opno;
+ saopexpr->useOr = true;
+ saopexpr->inputcollid = gentry->collation;
+ saopexpr->args = list_make2(gentry->node, newa);
+ saopexpr->location = -1;
+
+
+ or_list = lappend(or_list, (void *) saopexpr);
+
+ *something_changed = true;
+ change_apply = true;
+
+ }
+ else
+ {
+ list_free(gentry->consts);
+ or_list = lappend(or_list, (void *) gentry->rinfo->clause);
+ }
+ hash_search(or_group_htab, &gentry->key, HASH_REMOVE, NULL);
+ }
+
+ hash_destroy(or_group_htab);
+
+ if (!change_apply)
+ {
+ /*
+ * Each group contains only one element - use rinfo as is.
+ */
+ modified_rinfo = lappend(modified_rinfo, rinfo);
+ list_free(or_list);
+ continue;
+ }
+
+ /*
+ * Make a new version of the restriction. Remember source restriction
+ * can be used in another path (SeqScan, for example).
+ */
+
+ /* One more trick: assemble correct clause */
+ rinfo = make_restrictinfo(root,
+ list_length(or_list) > 1 ? makeBoolExpr(OR_EXPR, or_list, ((BoolExpr *) rinfo->clause)->location) :
+ linitial(or_list),
+ rinfo->is_pushed_down,
+ rinfo->has_clone,
+ rinfo->is_clone,
+ rinfo->pseudoconstant,
+ rinfo->security_level,
+ rinfo->required_relids,
+ rinfo->incompatible_relids,
+ rinfo->outer_relids);
+ rinfo->eval_cost=rinfo_base->eval_cost;
+ rinfo->norm_selec=rinfo_base->norm_selec;
+ rinfo->outer_selec=rinfo_base->outer_selec;
+ rinfo->left_bucketsize=rinfo_base->left_bucketsize;
+ rinfo->right_bucketsize=rinfo_base->right_bucketsize;
+ rinfo->left_mcvfreq=rinfo_base->left_mcvfreq;
+ rinfo->right_mcvfreq=rinfo_base->right_mcvfreq;
+ modified_rinfo = lappend(modified_rinfo, rinfo);
+ *something_changed = true;
+ }
+
+ /*
+ * Check if transformation has made. If nothing changed - return
+ * baserestrictinfo as is.
+ */
+ if (*something_changed)
+ {
+ return modified_rinfo;
+ }
+
+ list_free(modified_rinfo);
+ return baserestrictinfo;
+}
+
/*
* make_one_rel
* Finds all possible access paths for executing a query, returning a
@@ -767,6 +1157,12 @@ static void
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
Relids required_outer;
+ RelOptInfo *reL_alternative = makeNode(RelOptInfo);
+ //reL_alternative = copyObject(rel);
+ reL_alternative = copy_simple_rel(root, rel->relid, rel, reL_alternative);
+ reL_alternative->indexlist = NIL;
+ reL_alternative->baserestrictinfo = rel->baserestrictinfo;
+ reL_alternative->eclass_indexes = NULL;
/*
* We don't support pushing join clauses into the quals of a seqscan, but
@@ -787,6 +1183,39 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
/* Consider TID scans */
create_tidscan_paths(root, rel);
+
+ set_cheapest(rel);
+
+ if (rel->reloptkind == RELOPT_BASEREL && enable_or_transformation)
+ {
+ bool applied_transfomation;
+ applied_transfomation = (bool *) palloc(sizeof(bool));
+ reL_alternative->baserestrictinfo = transform_ors(root, reL_alternative->baserestrictinfo, &applied_transfomation);
+if (applied_transfomation)
+{
+ add_path(reL_alternative, create_seqscan_path(root, reL_alternative, required_outer, 0));
+
+ if (reL_alternative->consider_parallel && required_outer == NULL)
+ create_plain_partial_paths(root, reL_alternative);
+
+ create_index_paths(root, reL_alternative);
+
+ create_tidscan_paths(root, reL_alternative);
+
+ set_cheapest(reL_alternative);
+
+ elog(WARNING, "ors: - %f", rel->cheapest_total_path->total_cost);
+ elog(WARNING, "applied_transfomation: - %f", reL_alternative->cheapest_total_path->total_cost);
+
+ if (reL_alternative->cheapest_total_path->total_cost <= rel->cheapest_total_path->total_cost)
+ {
+ //copy_simple_rel(root, rel->relid, reL_alternative, rel);
+ rel->indexlist = reL_alternative->indexlist;
+ rel->baserestrictinfo = reL_alternative->baserestrictinfo;
+ rel->eclass_indexes = reL_alternative->eclass_indexes;
+ }
+ }
+ }
}
/*
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index acf4937db01..a9cc9a585df 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -34,12 +34,6 @@
#include "optimizer/restrictinfo.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
-#include "parser/parse_coerce.h"
-#include "common/hashfn.h"
-#include "catalog/pg_operator.h"
-#include "nodes/queryjumble.h"
-#include "utils/syscache.h"
-#include "catalog/namespace.h"
/* XXX see PartCollMatchesExprColl */
#define IndexCollMatchesExprColl(idxcollation, exprcollation) \
@@ -198,391 +192,6 @@ static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
EquivalenceClass *ec, EquivalenceMember *em,
void *arg);
-bool enable_or_transformation = true;
-typedef struct OrClauseGroupKey
-{
- Expr *expr; /* Pointer to the expression tree which has been a source for
- the hashkey value */
- Oid opno;
- Oid exprtype;
-} OrClauseGroupKey;
-
-typedef struct OrClauseGroupEntry
-{
- OrClauseGroupKey key;
- Node *node;
- List *consts;
- Oid collation;
- RestrictInfo *rinfo;
- List *exprs;
-} OrClauseGroupEntry;
-
-/*
- * Hash function that's compatible with guc_name_compare
- */
-static uint32
-orclause_hash(const void *data, Size keysize)
-{
- OrClauseGroupKey *key = (OrClauseGroupKey *) data;
- uint64 hash;
-
- (void) JumbleExpr(key->expr, &hash);
- hash = ((uint64) key->opno + (uint64) key->exprtype) % UINT64_MAX;
- return hash;
-}
-
-static void *
-orclause_keycopy(void *dest, const void *src, Size keysize)
-{
- OrClauseGroupKey *src_key = (OrClauseGroupKey *) src;
- OrClauseGroupKey *dst_key = (OrClauseGroupKey *) dest;
-
- Assert(sizeof(OrClauseGroupKey) == keysize);
-
- dst_key->expr = src_key->expr;
- dst_key->opno = src_key->opno;
- dst_key->exprtype = src_key->exprtype;
- return dst_key;
-}
-
-/*
- * Dynahash match function to use in guc_hashtab
- */
-static int
-orclause_match(const void *data1, const void *data2, Size keysize)
-{
- OrClauseGroupKey *key1 = (OrClauseGroupKey *) data1;
- OrClauseGroupKey *key2 = (OrClauseGroupKey *) data2;
-
- Assert(sizeof(OrClauseGroupKey) == keysize);
-
- if (key1->opno == key2->opno && key1->exprtype == key2->exprtype &&
- equal(key1->expr, key2->expr))
- return 0;
-
- return 1;
-}
-
-/*
- * Pass through baserestrictinfo clauses and try to convert OR clauses into IN
- * Return a modified clause list or just the same baserestrictinfo, if no
- * changes have made.
- * XXX: do not change source list of clauses at all.
- */
-static List *
-transform_ors(PlannerInfo *root, List *baserestrictinfo)
-{
- ListCell *lc;
- List *modified_rinfo = NIL;
- bool something_changed = false;
-
- /*
- * Complexity of a clause could be arbitrarily sophisticated. Here, we will
- * look up only on the top level of clause list.
- * XXX: It is substantiated? Could we change something here?
- */
- foreach (lc, baserestrictinfo)
- {
- RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
- RestrictInfo *rinfo_base = copyObject(rinfo);
- List *or_list = NIL;
- ListCell *lc_eargs,
- *lc_rargs;
- bool change_apply = false;
- HASHCTL info;
- HTAB *or_group_htab = NULL;
- int len_ors;
- HASH_SEQ_STATUS hash_seq;
- OrClauseGroupEntry *gentry;
-
- if (!enable_or_transformation || !restriction_is_or_clause(rinfo))
- {
- /* Add a clause without changes */
- modified_rinfo = lappend(modified_rinfo, rinfo);
- continue;
- }
-
- len_ors = ((BoolExpr *) rinfo->clause)->args? list_length(((BoolExpr *) rinfo->clause)->args) : 0;
-
- if (len_ors < 2)
- {
- /* Add a clause without changes */
- modified_rinfo = lappend(modified_rinfo, rinfo);
- continue;
- }
-
- MemSet(&info, 0, sizeof(info));
- info.keysize = sizeof(OrClauseGroupKey);
- info.entrysize = sizeof(OrClauseGroupEntry);
- info.hash = orclause_hash;
- info.keycopy = orclause_keycopy;
- info.match = orclause_match;
- or_group_htab = hash_create("OR Groups",
- len_ors,
- &info,
- HASH_ELEM | HASH_FUNCTION | HASH_COMPARE | HASH_KEYCOPY);
-
- /*
- * NOTE:
- * It is an OR-clause. So, rinfo->orclause is a BoolExpr node, contains
- * a list of sub-restrictinfo args, and rinfo->clause - which is the
- * same expression, made from bare clauses. To not break selectivity
- * caches and other optimizations, use both:
- * - use rinfos from orclause if no transformation needed
- * - use bare quals from rinfo->clause in the case of transformation,
- * to create new RestrictInfo: in this case we have no options to avoid
- * selectivity estimation procedure.
- */
- forboth(lc_eargs, ((BoolExpr *) rinfo->clause)->args,
- lc_rargs, ((BoolExpr *) rinfo->orclause)->args)
- {
- Expr *or_qual = (Expr *) lfirst(lc_eargs);
- RestrictInfo *sub_rinfo;
- Node *const_expr;
- Node *nconst_expr;
- OrClauseGroupKey hashkey;
- bool found = false;
-
- /* It may be one more boolean expression, skip it for now */
- if (!IsA(lfirst(lc_rargs), RestrictInfo))
- {
- or_list = lappend(or_list, (void *) or_qual);
- continue;
- }
-
- sub_rinfo = lfirst_node(RestrictInfo, lc_rargs);
-
- /* Check: it is an expr of the form 'F(x) oper ConstExpr' */
- if (!IsA(or_qual, OpExpr) ||
- !(bms_is_empty(sub_rinfo->left_relids) ^
- bms_is_empty(sub_rinfo->right_relids)) ||
- contain_volatile_functions((Node *) or_qual))
- {
- /* Again, it's not the expr we can transform */
- or_list = lappend(or_list, (void *) or_qual);
- continue;
- }
-
- /* Get pointers to constant and expression sides of the clause */
- const_expr =bms_is_empty(sub_rinfo->left_relids) ?
- get_leftop(sub_rinfo->clause) :
- get_rightop(sub_rinfo->clause);
- nconst_expr = bms_is_empty(sub_rinfo->left_relids) ?
- get_rightop(sub_rinfo->clause) :
- get_leftop(sub_rinfo->clause);
-
- if (!op_mergejoinable(((OpExpr *) sub_rinfo->clause)->opno, exprType(nconst_expr)))
- {
- /* And again, filter out non-equality operators */
- or_list = lappend(or_list, (void *) or_qual);
- continue;
- }
-
- /*
- * At this point we definitely have a transformable clause.
- * Classify it and add into specific group of clauses, or create new
- * group.
- */
- hashkey.expr = (Expr *) nconst_expr;
- hashkey.opno = ((OpExpr *) or_qual)->opno;
- hashkey.exprtype = exprType(nconst_expr);
- gentry = hash_search(or_group_htab, &hashkey, HASH_ENTER, &found);
-
- if (unlikely(found))
- {
- gentry->consts = lappend(gentry->consts, const_expr);
- gentry->exprs = lappend(gentry->exprs, or_qual);
- }
- else
- {
- gentry->node = nconst_expr;
- gentry->consts = list_make1(const_expr);
- gentry->exprs = list_make1(or_qual);
- gentry->rinfo = sub_rinfo;
- gentry->collation = exprInputCollation((Node *)sub_rinfo->clause);
- }
- }
-
- if (list_length(or_list) == len_ors)
- {
- /*
- * No any transformations possible with this list of arguments. Here we
- * already made all underlying transformations. Thus, just return the
- * transformed bool expression.
- */
- modified_rinfo = lappend(modified_rinfo, rinfo);
- continue;
- }
-
- if (!or_group_htab && hash_get_num_entries(or_group_htab) < 1)
- {
- /*
- * No any transformations possible with this rinfo, just add itself
- * to the list and go further.
- */
- modified_rinfo = lappend(modified_rinfo, rinfo);
- continue;
- }
-
- hash_seq_init(&hash_seq, or_group_htab);
-
- /* Let's convert each group of clauses to an IN operation. */
-
- /*
- * Go through the list of groups and convert each, where number of
- * consts more than 1. trivial groups move to OR-list again
- */
-
- while ((gentry = (OrClauseGroupEntry *) hash_seq_search(&hash_seq)) != NULL)
- {
- Oid scalar_type;
- Oid array_type;
-
- Assert(list_length(gentry->consts) > 0);
- Assert(list_length(gentry->exprs) == list_length(gentry->consts));
-
- if (list_length(gentry->consts) == 1)
- {
- /*
- * Only one element in the class. Return rinfo into the BoolExpr
- * args list unchanged.
- */
- list_free(gentry->consts);
- or_list = list_concat(or_list, gentry->exprs);
- continue;
- }
-
- /*
- * Do the transformation.
- *
- * First of all, try to select a common type for the array elements.
- * Note that since the LHS' type is first in the list, it will be
- * preferred when there is doubt (eg, when all the RHS items are
- * unknown literals).
- *
- * Note: use list_concat here not lcons, to avoid damaging rnonvars.
- *
- * As a source of insides, use make_scalar_array_op()
- */
- scalar_type = gentry->key.exprtype;
-
- if (scalar_type != RECORDOID && OidIsValid(scalar_type))
- array_type = get_array_type(scalar_type);
- else
- array_type = InvalidOid;
-
- if (array_type != InvalidOid && scalar_type != InvalidOid)
- {
- /*
- * OK: coerce all the right-hand non-Var inputs to the common
- * type and build an ArrayExpr for them.
- */
- List *aexprs = NULL;
- ArrayExpr *newa = NULL;
- ScalarArrayOpExpr *saopexpr = NULL;
- HeapTuple opertup;
- Form_pg_operator operform;
- List *namelist = NIL;
-
- aexprs = gentry->consts;
-
- newa = makeNode(ArrayExpr);
- /* array_collid will be set by parse_collate.c */
- newa->element_typeid = scalar_type;
- newa->array_typeid = array_type;
- newa->multidims = false;
- newa->elements = aexprs;
- newa->location = -1;
-
- opertup = SearchSysCache1(OPEROID,
- ObjectIdGetDatum(gentry->key.opno));
-
- if (!HeapTupleIsValid(opertup))
- elog(ERROR, "cache lookup failed for operator %u",
- gentry->key.opno);
-
- operform = (Form_pg_operator) GETSTRUCT(opertup);
- if (!OperatorIsVisible(gentry->key.opno))
- namelist = lappend(namelist, makeString(get_namespace_name(operform->oprnamespace)));
-
- namelist = lappend(namelist, makeString(pstrdup(NameStr(operform->oprname))));
- ReleaseSysCache(opertup);
-
- saopexpr = makeNode(ScalarArrayOpExpr);
- saopexpr->opno = ((OpExpr *) gentry->rinfo->clause)->opno;
- saopexpr->useOr = true;
- saopexpr->inputcollid = gentry->collation;
- saopexpr->args = list_make2(gentry->node, newa);
- saopexpr->location = -1;
-
-
- or_list = lappend(or_list, (void *) saopexpr);
-
- something_changed = true;
- change_apply = true;
-
- }
- else
- {
- list_free(gentry->consts);
- or_list = lappend(or_list, (void *) gentry->rinfo->clause);
- }
- hash_search(or_group_htab, &gentry->key, HASH_REMOVE, NULL);
- }
-
- hash_destroy(or_group_htab);
-
- if (!change_apply)
- {
- /*
- * Each group contains only one element - use rinfo as is.
- */
- modified_rinfo = lappend(modified_rinfo, rinfo);
- list_free(or_list);
- continue;
- }
-
- /*
- * Make a new version of the restriction. Remember source restriction
- * can be used in another path (SeqScan, for example).
- */
-
- /* One more trick: assemble correct clause */
- rinfo = make_restrictinfo(root,
- list_length(or_list) > 1 ? makeBoolExpr(OR_EXPR, or_list, ((BoolExpr *) rinfo->clause)->location) :
- linitial(or_list),
- rinfo->is_pushed_down,
- rinfo->has_clone,
- rinfo->is_clone,
- rinfo->pseudoconstant,
- rinfo->security_level,
- rinfo->required_relids,
- rinfo->incompatible_relids,
- rinfo->outer_relids);
- rinfo->eval_cost=rinfo_base->eval_cost;
- rinfo->norm_selec=rinfo_base->norm_selec;
- rinfo->outer_selec=rinfo_base->outer_selec;
- rinfo->left_bucketsize=rinfo_base->left_bucketsize;
- rinfo->right_bucketsize=rinfo_base->right_bucketsize;
- rinfo->left_mcvfreq=rinfo_base->left_mcvfreq;
- rinfo->right_mcvfreq=rinfo_base->right_mcvfreq;
- modified_rinfo = lappend(modified_rinfo, rinfo);
- something_changed = true;
- }
-
- /*
- * Check if transformation has made. If nothing changed - return
- * baserestrictinfo as is.
- */
- if (something_changed)
- {
- return modified_rinfo;
- }
-
- list_free(modified_rinfo);
- return baserestrictinfo;
-}
-
/*
* create_index_paths()
* Generate all interesting index paths for the given relation.
@@ -1108,9 +717,6 @@ get_index_paths(PlannerInfo *root, RelOptInfo *rel,
bool skip_lower_saop = false;
ListCell *lc;
- if (rel->reloptkind == RELOPT_BASEREL)
- rel->baserestrictinfo = transform_ors(root, rel->baserestrictinfo);
-
/*
* Build simple index paths using the clauses. Allow ScalarArrayOpExpr
* clauses only if the index AM supports them natively, and skip any such
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 5d83f60eb9a..189cee449bf 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -183,6 +183,97 @@ expand_planner_arrays(PlannerInfo *root, int add_size)
root->simple_rel_array_size = new_size;
}
+RelOptInfo *
+copy_simple_rel(PlannerInfo *root, int relid, RelOptInfo *base_rel, RelOptInfo *rel)
+{
+ rel->reloptkind = base_rel->parent ? RELOPT_OTHER_MEMBER_REL : RELOPT_BASEREL;
+ rel->relids = base_rel->relids;
+ rel->rows = base_rel->rows;
+ /* cheap startup cost is interesting iff not all tuples to be retrieved */
+ rel->consider_startup = (root->tuple_fraction > 0);
+ rel->consider_param_startup = base_rel->consider_param_startup; /* might get changed later */
+ rel->consider_parallel = base_rel->consider_parallel; /* might get changed later */
+ rel->reltarget = base_rel->reltarget;
+ rel->pathlist = base_rel->pathlist;
+ rel->ppilist = base_rel->ppilist;
+ rel->partial_pathlist = base_rel->partial_pathlist;
+ rel->cheapest_startup_path = base_rel->cheapest_startup_path;
+ rel->cheapest_total_path = base_rel->cheapest_total_path;
+ rel->cheapest_unique_path = base_rel->cheapest_unique_path;
+ rel->cheapest_parameterized_paths = base_rel->cheapest_parameterized_paths;
+ rel->relid = base_rel->relid;
+ /* min_attr, max_attr, attr_needed, attr_widths are set below */
+ rel->lateral_vars = base_rel->lateral_vars;
+ rel->statlist = base_rel->statlist;
+ rel->pages = base_rel->pages;
+ rel->tuples = base_rel->tuples;
+ rel->allvisfrac = base_rel->allvisfrac;
+ rel->subroot = base_rel->subroot;
+ rel->subplan_params = base_rel->subplan_params;
+ rel->rel_parallel_workers = base_rel->rel_parallel_workers; /* set up in get_relation_info */
+ rel->amflags = base_rel->amflags;
+ rel->parent = base_rel->parent;
+ rel->serverid = base_rel->serverid;
+ rel->userid = base_rel->userid;
+ rel->useridiscurrent = base_rel->useridiscurrent;
+ rel->fdwroutine = base_rel->fdwroutine;
+ rel->fdw_private = base_rel->fdw_private;
+ rel->unique_for_rels = base_rel->unique_for_rels;
+ rel->non_unique_for_rels = base_rel->non_unique_for_rels;
+ rel->baserestrictcost.startup = 0;
+ rel->baserestrictcost.per_tuple = 0;
+ rel->baserestrict_min_security = UINT_MAX;
+ rel->joininfo = base_rel->joininfo;
+ rel->has_eclass_joins = base_rel->has_eclass_joins;
+
+ /*
+ * Pass assorted information down the inheritance hierarchy.
+ */
+ if (base_rel->parent)
+ {
+ /* We keep back-links to immediate parent and topmost parent. */
+ rel->parent = base_rel->parent;
+ rel->top_parent = base_rel->parent->top_parent ? base_rel->parent->top_parent : base_rel->parent;
+ rel->top_parent_relids = base_rel->top_parent->relids;
+
+ /*
+ * A child rel is below the same outer joins as its parent. (We
+ * presume this info was already calculated for the parent.)
+ */
+ rel->nulling_relids = base_rel->parent->nulling_relids;
+
+ /*
+ * Also propagate lateral-reference information from appendrel parent
+ * rels to their child rels. We intentionally give each child rel the
+ * same minimum parameterization, even though it's quite possible that
+ * some don't reference all the lateral rels. This is because any
+ * append path for the parent will have to have the same
+ * parameterization for every child anyway, and there's no value in
+ * forcing extra reparameterize_path() calls. Similarly, a lateral
+ * reference to the parent prevents use of otherwise-movable join rels
+ * for each child.
+ *
+ * It's possible for child rels to have their own children, in which
+ * case the topmost parent's lateral info propagates all the way down.
+ */
+ rel->direct_lateral_relids = base_rel->parent->direct_lateral_relids;
+ rel->lateral_relids = base_rel->parent->lateral_relids;
+ rel->lateral_referencers = base_rel->parent->lateral_referencers;
+ }
+ else
+ {
+ rel->parent = base_rel->parent;
+ rel->top_parent = base_rel->top_parent;
+ rel->top_parent_relids = base_rel->top_parent_relids;
+ rel->nulling_relids = base_rel->nulling_relids;
+ rel->direct_lateral_relids = base_rel->direct_lateral_relids;
+ rel->lateral_relids = base_rel->lateral_relids;
+ rel->lateral_referencers = base_rel->lateral_referencers;
+ }
+
+ return rel;
+}
+
/*
* build_simple_rel
* Construct a new RelOptInfo for a base relation or 'other' relation.
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 6e557bebc44..2b3825b6e52 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -304,6 +304,8 @@ extern Path *reparameterize_path_by_child(PlannerInfo *root, Path *path,
*/
extern void setup_simple_rel_arrays(PlannerInfo *root);
extern void expand_planner_arrays(PlannerInfo *root, int add_size);
+extern RelOptInfo *
+copy_simple_rel(PlannerInfo *root, int relid, RelOptInfo *base_rel, RelOptInfo *rel);
extern RelOptInfo *build_simple_rel(PlannerInfo *root, int relid,
RelOptInfo *parent);
extern RelOptInfo *find_base_rel(PlannerInfo *root, int relid);
--
2.34.1