or_patch_bitmapindex_create.diff
text/x-patch
Filename: or_patch_bitmapindex_create.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
| File | + | − |
|---|---|---|
| src/backend/optimizer/path/indxpath.c | 410 | 29 |
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 03a5fbdc6dc..754c83816f7 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -34,6 +34,11 @@
#include "optimizer/restrictinfo.h"
#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
+#include "common/hashfn.h"
+#include "utils/lsyscache.h"
+#include "parser/parse_expr.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_oper.h"
/* XXX see PartCollMatchesExprColl */
@@ -73,6 +78,36 @@ typedef struct
int indexcol; /* index column we want to match to */
} ec_member_matches_arg;
+typedef struct OrClauseGroupEntry
+{
+ char *hash_leftvar_key;
+
+ Node *node;
+ List *consts;
+ Oid scalar_type;
+ Oid opno;
+ Node *expr;
+ RestrictInfo *ri;
+} OrClauseGroupEntry;
+
+static int
+or_name_match(const void *key1, const void *key2, Size keysize)
+{
+ const char *name1 = *(const char *const *) key1;
+ const char *name2 = *(const char *const *) key2;
+
+ return strcmp(name1, name2);
+}
+
+static uint32
+or_name_hash(const void *key, Size keysize)
+{
+ const char *name = *(const char *const *) key;
+
+ return DatumGetInt32(hash_any((unsigned char *)name, strlen(name)));
+}
+
+bool enable_or_transformation = true;
static void consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel,
IndexOptInfo *index,
@@ -193,6 +228,55 @@ static bool ec_member_matches_indexcol(PlannerInfo *root, RelOptInfo *rel,
EquivalenceClass *ec, EquivalenceMember *em,
void *arg);
+static char *
+get_key_nconst_node(Node *nconst_node)
+{
+ if (IsA(nconst_node, OpExpr))
+ {
+ OpExpr *clause = (OpExpr*) nconst_node;
+ OpExpr *temp = makeNode(OpExpr);
+
+ temp->opno = clause->opno;
+ temp->opfuncid = InvalidOid;
+ temp->opresulttype = clause->opresulttype;
+ temp->opretset = clause->opretset;
+ temp->opcollid = clause->opcollid;
+ temp->inputcollid = clause->inputcollid;
+ temp->location = -1;
+
+ temp->args = list_copy(clause->args);
+ return nodeToString(temp);
+ }
+ else if (IsA(nconst_node, Var))
+ {
+ Var *clause = (Var*) nconst_node;
+ Var *var = makeNode(Var);
+
+ var->varno = clause->varno;
+ var->varattno = clause->varattno;
+ var->vartype = clause->vartype;
+ var->vartypmod = clause->vartypmod;
+ var->varcollid = clause->varcollid;
+ var->varlevelsup = clause->varlevelsup;
+ var->varattnosyn = clause->varattno;
+ var->location = -1;
+
+ return nodeToString(var);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/*
+ * 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.
+ */
+
+
/*
* create_index_paths()
@@ -250,6 +334,13 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
/* Bitmap paths are collected and then dealt with at the end */
bitindexpaths = bitjoinpaths = joinorclauses = NIL;
+ /*
+ * Generate BitmapOrPaths for any suitable OR-clauses present in the
+ * restriction list. Add these to bitindexpaths.
+ */
+ indexpaths = generate_bitmap_or_paths(root, rel,
+ rel->baserestrictinfo, NIL);
+
/* Examine each index in turn */
foreach(lc, rel->indexlist)
{
@@ -309,13 +400,7 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
&eclauseset,
&bitjoinpaths);
}
-
- /*
- * Generate BitmapOrPaths for any suitable OR-clauses present in the
- * restriction list. Add these to bitindexpaths.
- */
- indexpaths = generate_bitmap_or_paths(root, rel,
- rel->baserestrictinfo, NIL);
+
bitindexpaths = list_concat(bitindexpaths, indexpaths);
/*
@@ -1237,6 +1322,7 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
List *result = NIL;
List *all_clauses;
ListCell *lc;
+ List *modified_rinfo = NIL;
/*
* We can use both the current and other clauses as context for
@@ -1247,23 +1333,45 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
foreach(lc, clauses)
{
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc);
- List *pathlist;
+ List *pathlist = NIL;
Path *bitmapqual;
- ListCell *j;
+ ListCell *lc_rargs, *lc_eargs;
+ HASHCTL info;
+ HTAB *or_group_htab = NULL;
+ int len_ors = 0;
+ List *or_list = NIL;
/* Ignore RestrictInfos that aren't ORs */
if (!restriction_is_or_clause(rinfo))
+ {
+ modified_rinfo = lappend(modified_rinfo, rinfo);
continue;
+ }
+
+ len_ors = list_length(((BoolExpr *) rinfo->orclause)->args);
+
+ MemSet(&info, 0, sizeof(info));
+ info.keysize = sizeof(char *);
+ info.entrysize = sizeof(OrClauseGroupEntry);
+ info.hash = or_name_hash;
+ info.match = or_name_match;
+ or_group_htab = hash_create("OR Groups",
+ len_ors,
+ &info,
+ HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
/*
* We must be able to match at least one index to each of the arms of
* the OR, else we can't use it.
*/
pathlist = NIL;
- foreach(j, ((BoolExpr *) rinfo->orclause)->args)
+ forboth(lc_eargs, ((BoolExpr *) rinfo->clause)->args,
+ lc_rargs, ((BoolExpr *) rinfo->orclause)->args)
{
- Node *orarg = (Node *) lfirst(j);
+ Node *orarg = (Node *) lfirst(lc_rargs);
List *indlist;
+ OrClauseGroupEntry *gentry;
+ Node *orqual = (Node *) lfirst(lc_eargs);
/* OR arguments should be ANDs or sub-RestrictInfos */
if (is_andclause(orarg))
@@ -1279,36 +1387,309 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
generate_bitmap_or_paths(root, rel,
andargs,
all_clauses));
+ or_list = lappend(or_list, orqual);
}
else
{
RestrictInfo *ri = castNode(RestrictInfo, orarg);
- List *orargs;
+ RestrictInfo *sub_rinfo = lfirst_node(RestrictInfo, lc_rargs);
+ Node *nconst_expr = NULL;
+ Node *const_expr = NULL;
+ char *str = NULL;
+ bool found;
Assert(!restriction_is_or_clause(ri));
- orargs = list_make1(ri);
- indlist = build_paths_for_OR(root, rel,
- orargs,
- all_clauses);
+ if (!IsA(lfirst(lc_rargs), RestrictInfo) || !enable_or_transformation ||
+ /* Check: it is an expr of the form 'F(x) oper ConstExpr' */
+ !IsA(sub_rinfo->clause, OpExpr) ||
+ !(bms_is_empty(sub_rinfo->left_relids) ^
+ bms_is_empty(sub_rinfo->right_relids)) ||
+ contain_volatile_functions((Node *) orarg))
+ {
+ List *orargs;
+ orargs = list_make1(ri);
+ indlist = build_paths_for_OR(root, rel,
+ orargs,
+ all_clauses);
+ /*
+ * If nothing matched this arm, we can't do anything with this OR
+ * clause.
+ */
+ if (indlist == NIL)
+ {
+ pathlist = NIL;
+ break;
+ }
+
+ /*
+ * OK, pick the most promising AND combination, and add it to
+ * pathlist.
+ */
+ bitmapqual = choose_bitmap_and(root, rel, indlist);
+ pathlist = lappend(pathlist, bitmapqual);
+ or_list = lappend(or_list, orqual);
+ continue;
+ }
+
+ /*
+ * Detect the constant side of the clause. Recall non-constant
+ * expression can be made not only with Vars, but also with Params,
+ * which is not bonded with any relation. Thus, we detect the const
+ * side - if another side is constant too, the orqual couldn't be
+ * an OpExpr.
+ * Get pointers to constant and expression sides of the qual.
+ */
+ 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);
+
+ str = get_key_nconst_node(nconst_expr);
+
+ if (!op_mergejoinable(((OpExpr*)sub_rinfo->clause)->opno, exprType(nconst_expr)) || str == NULL)
+ {
+ List *orargs;
+ orargs = list_make1(ri);
+ indlist = build_paths_for_OR(root, rel,
+ orargs,
+ all_clauses);
+ /*
+ * If nothing matched this arm, we can't do anything with this OR
+ * clause.
+ */
+ if (indlist == NIL)
+ {
+ pathlist = NIL;
+ break;
+ }
+
+ /*
+ * OK, pick the most promising AND combination, and add it to
+ * pathlist.
+ */
+ bitmapqual = choose_bitmap_and(root, rel, indlist);
+ pathlist = lappend(pathlist, bitmapqual);
+ or_list = lappend(or_list, orqual);
+ continue;
+ }
+
+ /*
+ * At this point we definitely have a transformable clause.
+ * Classify it and add into specific group of clauses, or create new
+ * group.
+ * TODO: to manage complexity in the case of many different clauses
+ * (X1=C1) OR (X2=C2 OR) ... (XN = CN) we could invent something
+ * like a hash table. But also we believe, that the case of many
+ * different variable sides is very rare.
+ */
+ gentry = hash_search(or_group_htab, &str, HASH_FIND, &found);
+
+ if (found)
+ {
+ gentry->consts = lappend(gentry->consts, const_expr);
+ /*
+ * The clause classified successfully and added into existed
+ * clause group.
+ */
+ continue;
+ }
+
+ /* New clause group needed */
+ gentry = hash_search(or_group_htab, &str, HASH_ENTER, &found);
+ gentry->node = nconst_expr;
+ gentry->consts = list_make1(const_expr);
+ gentry->expr = orqual;
+ gentry->hash_leftvar_key = str;
+ gentry->ri = ri;
}
+ }
+
+ if (!(or_group_htab && (hash_get_num_entries(or_group_htab) < 1 ||
+ hash_get_num_entries(or_group_htab) == len_ors)))
+ {
+ HASH_SEQ_STATUS hash_seq;
+ OrClauseGroupEntry *gentry;
+ bool success = false;
+
+ hash_seq_init(&hash_seq, or_group_htab);
+
+ /* Let's convert each group of clauses to an IN operation. */
/*
- * If nothing matched this arm, we can't do anything with this OR
- * clause.
- */
- if (indlist == NIL)
+ * 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)
{
- pathlist = NIL;
- break;
+ List *allexprs;
+ Oid scalar_type;
+ Oid array_type;
+ List *orargs;
+ List *indlist;
+
+ Assert(list_length(gentry->consts) > 0);
+
+ if (list_length(gentry->consts) == 1)
+ {
+ /*
+ * Only one element in the class. Return rinfo into the BoolExpr
+ * args list unchanged.
+ */
+ list_free(gentry->consts);
+
+ orargs = list_make1(gentry->ri);
+ indlist = build_paths_for_OR(root, rel,
+ orargs,
+ all_clauses);
+ /*
+ * If nothing matched this arm, we can't do anything with this OR
+ * clause.
+ */
+ if (indlist == NIL)
+ {
+ pathlist = NIL;
+ break;
+ }
+
+ /*
+ * OK, pick the most promising AND combination, and add it to
+ * pathlist.
+ */
+ bitmapqual = choose_bitmap_and(root, rel, indlist);
+ pathlist = lappend(pathlist, bitmapqual);
+ or_list = lappend(or_list, gentry->expr);
+ 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()
+ */
+ allexprs = list_concat(list_make1(gentry->node), gentry->consts);
+ scalar_type = select_common_type(NULL, allexprs, NULL, NULL);
+
+ if (scalar_type != RECORDOID && OidIsValid(scalar_type))
+ array_type = get_array_type(scalar_type);
+ else
+ array_type = InvalidOid;
+
+ if (array_type != InvalidOid)
+ {
+ /*
+ * OK: coerce all the right-hand non-Var inputs to the common
+ * type and build an ArrayExpr for them.
+ */
+ List *aexprs = NIL;
+ ArrayExpr *newa = NULL;
+ ScalarArrayOpExpr *saopexpr = NULL;
+ ListCell *l;
+
+ aexprs = NIL;
+
+ foreach(l, gentry->consts)
+ {
+ Node *rexpr = (Node *) lfirst(l);
+
+ rexpr = coerce_to_common_type(NULL, rexpr,
+ scalar_type,
+ "IN");
+ aexprs = lappend(aexprs, rexpr);
+ }
+
+ 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;
+
+ saopexpr =
+ (ScalarArrayOpExpr *)
+ make_scalar_array_op(NULL,
+ list_make1(makeString((char *) "=")),
+ true,
+ gentry->node,
+ (Node *) newa,
+ -1);
+ or_list = lappend(or_list, (void *) saopexpr);
+ success = true;
+ }
+ else
+ {
+ orargs = list_make1(gentry->ri);
+ indlist = build_paths_for_OR(root, rel,
+ orargs,
+ all_clauses);
+ list_free(gentry->consts);
+ /*
+ * If nothing matched this arm, we can't do anything with this OR
+ * clause.
+ */
+ if (indlist == NIL)
+ {
+ pathlist = NIL;
+ break;
+ }
+
+ /*
+ * OK, pick the most promising AND combination, and add it to
+ * pathlist.
+ */
+ bitmapqual = choose_bitmap_and(root, rel, indlist);
+ pathlist = lappend(pathlist, bitmapqual);
+ or_list = lappend(or_list, gentry->expr);
+ }
+ hash_search(or_group_htab, &gentry->hash_leftvar_key, HASH_REMOVE, NULL);
}
+ hash_destroy(or_group_htab);
- /*
- * OK, pick the most promising AND combination, and add it to
- * pathlist.
- */
- bitmapqual = choose_bitmap_and(root, rel, indlist);
- pathlist = lappend(pathlist, bitmapqual);
+ if (!success)
+ {
+ /*
+ * Each group contains only one element - use rinfo as is.
+ */
+ modified_rinfo = lappend(modified_rinfo, rinfo);
+ list_free(or_list);
+ }
+ else
+ {
+ RestrictInfo *ri;
+ List *orargs;
+ List *indlist;
+ ri = make_restrictinfo(root,
+ list_length(or_list) > 1 ? make_orclause(or_list) :
+ (Expr *) linitial(or_list),
+ ri->is_pushed_down,
+ ri->has_clone,
+ ri->is_clone,
+ ri->pseudoconstant,
+ ri->security_level,
+ ri->required_relids,
+ ri->incompatible_relids,
+ ri->outer_relids);
+
+ ri->eval_cost=rinfo->eval_cost;
+ ri->norm_selec=rinfo->norm_selec;
+ ri->outer_selec=rinfo->outer_selec;
+ ri->left_bucketsize=rinfo->left_bucketsize;
+ ri->right_bucketsize=rinfo->right_bucketsize;
+ ri->left_mcvfreq=rinfo->left_mcvfreq;
+ ri->right_mcvfreq=rinfo->right_mcvfreq;
+ modified_rinfo = lappend(modified_rinfo, ri);
+ }
}
/*
@@ -1322,10 +1703,10 @@ generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,
}
}
+ rel->baserestrictinfo = modified_rinfo;
return result;
}
-
/*
* choose_bitmap_and
* Given a nonempty list of bitmap paths, AND them into one path.