mirror of
https://github.com/postgres/postgres.git
synced 2025-07-07 00:36:50 +03:00
Refactor the representation of indexable clauses in IndexPaths.
In place of three separate but interrelated lists (indexclauses, indexquals, and indexqualcols), an IndexPath now has one list "indexclauses" of IndexClause nodes. This holds basically the same information as before, but in a more useful format: in particular, there is now a clear connection between an indexclause (an original restriction clause from WHERE or JOIN/ON) and the indexquals (directly usable index conditions) derived from it. We also change the ground rules a bit by mandating that clause commutation, if needed, be done up-front so that what is stored in the indexquals list is always directly usable as an index condition. This gets rid of repeated re-determination of which side of the clause is the indexkey during costing and plan generation, as well as repeated lookups of the commutator operator. To minimize the added up-front cost, the typical case of commuting a plain OpExpr is handled by a new special-purpose function commute_restrictinfo(). For RowCompareExprs, generating the new clause properly commuted to begin with is not really any more complex than before, it's just different --- and we can save doing that work twice, as the pretty-klugy original implementation did. Tracking the connection between original and derived clauses lets us also track explicitly whether the derived clauses are an exact or lossy translation of the original. This provides a cheap solution to getting rid of unnecessary rechecks of boolean index clauses, which previously seemed like it'd be more expensive than it was worth. Another pleasant (IMO) side-effect is that EXPLAIN now always shows index clauses with the indexkey on the left; this seems less confusing. This commit leaves expand_indexqual_conditions() and some related functions in a slightly messy state. I didn't bother to change them any more than minimally necessary to work with the new data structure, because all that code is going to be refactored out of existence in a follow-on patch. Discussion: https://postgr.es/m/22182.1549124950@sss.pgh.pa.us
This commit is contained in:
@ -145,7 +145,7 @@ typedef struct
|
||||
QualCost total;
|
||||
} cost_qual_eval_context;
|
||||
|
||||
static List *extract_nonindex_conditions(List *qual_clauses, List *indexquals);
|
||||
static List *extract_nonindex_conditions(List *qual_clauses, List *indexclauses);
|
||||
static MergeScanSelCache *cached_scansel(PlannerInfo *root,
|
||||
RestrictInfo *rinfo,
|
||||
PathKey *pathkey);
|
||||
@ -517,18 +517,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
|
||||
{
|
||||
path->path.rows = path->path.param_info->ppi_rows;
|
||||
/* qpquals come from the rel's restriction clauses and ppi_clauses */
|
||||
qpquals = list_concat(
|
||||
extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
|
||||
path->indexquals),
|
||||
qpquals = list_concat(extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
|
||||
path->indexclauses),
|
||||
extract_nonindex_conditions(path->path.param_info->ppi_clauses,
|
||||
path->indexquals));
|
||||
path->indexclauses));
|
||||
}
|
||||
else
|
||||
{
|
||||
path->path.rows = baserel->rows;
|
||||
/* qpquals come from just the rel's restriction clauses */
|
||||
qpquals = extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
|
||||
path->indexquals);
|
||||
path->indexclauses);
|
||||
}
|
||||
|
||||
if (!enable_indexscan)
|
||||
@ -753,20 +752,19 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
|
||||
*
|
||||
* Given a list of quals to be enforced in an indexscan, extract the ones that
|
||||
* will have to be applied as qpquals (ie, the index machinery won't handle
|
||||
* them). The actual rules for this appear in create_indexscan_plan() in
|
||||
* createplan.c, but the full rules are fairly expensive and we don't want to
|
||||
* go to that much effort for index paths that don't get selected for the
|
||||
* final plan. So we approximate it as quals that don't appear directly in
|
||||
* indexquals and also are not redundant children of the same EquivalenceClass
|
||||
* as some indexqual. This method neglects some infrequently-relevant
|
||||
* considerations, specifically clauses that needn't be checked because they
|
||||
* are implied by an indexqual. It does not seem worth the cycles to try to
|
||||
* factor that in at this stage, even though createplan.c will take pains to
|
||||
* remove such unnecessary clauses from the qpquals list if this path is
|
||||
* selected for use.
|
||||
* them). Here we detect only whether a qual clause is directly redundant
|
||||
* with some indexclause. If the index path is chosen for use, createplan.c
|
||||
* will try a bit harder to get rid of redundant qual conditions; specifically
|
||||
* it will see if quals can be proven to be implied by the indexquals. But
|
||||
* it does not seem worth the cycles to try to factor that in at this stage,
|
||||
* since we're only trying to estimate qual eval costs. Otherwise this must
|
||||
* match the logic in create_indexscan_plan().
|
||||
*
|
||||
* qual_clauses, and the result, are lists of RestrictInfos.
|
||||
* indexclauses is a list of IndexClauses.
|
||||
*/
|
||||
static List *
|
||||
extract_nonindex_conditions(List *qual_clauses, List *indexquals)
|
||||
extract_nonindex_conditions(List *qual_clauses, List *indexclauses)
|
||||
{
|
||||
List *result = NIL;
|
||||
ListCell *lc;
|
||||
@ -777,10 +775,8 @@ extract_nonindex_conditions(List *qual_clauses, List *indexquals)
|
||||
|
||||
if (rinfo->pseudoconstant)
|
||||
continue; /* we may drop pseudoconstants here */
|
||||
if (list_member_ptr(indexquals, rinfo))
|
||||
continue; /* simple duplicate */
|
||||
if (is_redundant_derived_clause(rinfo, indexquals))
|
||||
continue; /* derived from same EquivalenceClass */
|
||||
if (is_redundant_with_indexclauses(rinfo, indexclauses))
|
||||
continue; /* dup or derived from same EquivalenceClass */
|
||||
/* ... skip the predicate proof attempt createplan.c will try ... */
|
||||
result = lappend(result, rinfo);
|
||||
}
|
||||
@ -4242,8 +4238,7 @@ has_indexed_join_quals(NestPath *joinpath)
|
||||
innerpath->parent->relids,
|
||||
joinrelids))
|
||||
{
|
||||
if (!(list_member_ptr(indexclauses, rinfo) ||
|
||||
is_redundant_derived_clause(rinfo, indexclauses)))
|
||||
if (!is_redundant_with_indexclauses(rinfo, indexclauses))
|
||||
return false;
|
||||
found_one = true;
|
||||
}
|
||||
|
@ -2511,3 +2511,40 @@ is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_redundant_with_indexclauses
|
||||
* Test whether rinfo is redundant with any clause in the IndexClause
|
||||
* list. Here, for convenience, we test both simple identity and
|
||||
* whether it is derived from the same EC as any member of the list.
|
||||
*/
|
||||
bool
|
||||
is_redundant_with_indexclauses(RestrictInfo *rinfo, List *indexclauses)
|
||||
{
|
||||
EquivalenceClass *parent_ec = rinfo->parent_ec;
|
||||
ListCell *lc;
|
||||
|
||||
foreach(lc, indexclauses)
|
||||
{
|
||||
IndexClause *iclause = lfirst_node(IndexClause, lc);
|
||||
RestrictInfo *otherrinfo = iclause->rinfo;
|
||||
|
||||
/* If indexclause is lossy, it won't enforce the condition exactly */
|
||||
if (iclause->lossy)
|
||||
continue;
|
||||
|
||||
/* Match if it's same clause (pointer equality should be enough) */
|
||||
if (rinfo == otherrinfo)
|
||||
return true;
|
||||
/* Match if derived from same EC */
|
||||
if (parent_ec && otherrinfo->parent_ec == parent_ec)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* No need to look at the derived clauses in iclause->indexquals; they
|
||||
* couldn't match if the parent clause didn't.
|
||||
*/
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ typedef enum
|
||||
typedef struct
|
||||
{
|
||||
bool nonempty; /* True if lists are not all empty */
|
||||
/* Lists of RestrictInfos, one per index column */
|
||||
/* Lists of IndexClause nodes, one list per index column */
|
||||
List *indexclauses[INDEX_MAX_KEYS];
|
||||
} IndexClauseSet;
|
||||
|
||||
@ -175,13 +175,19 @@ static bool match_boolean_index_clause(Node *clause, int indexcol,
|
||||
static bool match_special_index_operator(Expr *clause,
|
||||
Oid opfamily, Oid idxcollation,
|
||||
bool indexkey_on_left);
|
||||
static IndexClause *expand_indexqual_conditions(IndexOptInfo *index,
|
||||
int indexcol,
|
||||
RestrictInfo *rinfo);
|
||||
static Expr *expand_boolean_index_clause(Node *clause, int indexcol,
|
||||
IndexOptInfo *index);
|
||||
static List *expand_indexqual_opclause(RestrictInfo *rinfo,
|
||||
Oid opfamily, Oid idxcollation);
|
||||
Oid opfamily, Oid idxcollation,
|
||||
bool *lossy);
|
||||
static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
IndexOptInfo *index,
|
||||
int indexcol);
|
||||
int indexcol,
|
||||
List **indexcolnos,
|
||||
bool *lossy);
|
||||
static List *prefix_quals(Node *leftop, Oid opfamily, Oid collation,
|
||||
Const *prefix, Pattern_Prefix_Status pstatus);
|
||||
static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily,
|
||||
@ -496,7 +502,7 @@ consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel,
|
||||
*
|
||||
* 'rel', 'index', 'rclauseset', 'jclauseset', 'eclauseset', and
|
||||
* 'bitindexpaths' as above
|
||||
* 'indexjoinclauses' is a list of RestrictInfos for join clauses
|
||||
* 'indexjoinclauses' is a list of IndexClauses for join clauses
|
||||
* 'considered_clauses' is the total number of clauses considered (so far)
|
||||
* '*considered_relids' is a list of all relids sets already considered
|
||||
*/
|
||||
@ -516,8 +522,9 @@ consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel,
|
||||
/* Examine relids of each joinclause in the given list */
|
||||
foreach(lc, indexjoinclauses)
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
||||
Relids clause_relids = rinfo->clause_relids;
|
||||
IndexClause *iclause = (IndexClause *) lfirst(lc);
|
||||
Relids clause_relids = iclause->rinfo->clause_relids;
|
||||
EquivalenceClass *parent_ec = iclause->rinfo->parent_ec;
|
||||
ListCell *lc2;
|
||||
|
||||
/* If we already tried its relids set, no need to do so again */
|
||||
@ -558,8 +565,8 @@ consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel,
|
||||
* parameterization; so skip if any clause derived from the same
|
||||
* eclass would already have been included when using oldrelids.
|
||||
*/
|
||||
if (rinfo->parent_ec &&
|
||||
eclass_already_used(rinfo->parent_ec, oldrelids,
|
||||
if (parent_ec &&
|
||||
eclass_already_used(parent_ec, oldrelids,
|
||||
indexjoinclauses))
|
||||
continue;
|
||||
|
||||
@ -628,11 +635,11 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
/* First find applicable simple join clauses */
|
||||
foreach(lc, jclauseset->indexclauses[indexcol])
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
||||
IndexClause *iclause = (IndexClause *) lfirst(lc);
|
||||
|
||||
if (bms_is_subset(rinfo->clause_relids, relids))
|
||||
if (bms_is_subset(iclause->rinfo->clause_relids, relids))
|
||||
clauseset.indexclauses[indexcol] =
|
||||
lappend(clauseset.indexclauses[indexcol], rinfo);
|
||||
lappend(clauseset.indexclauses[indexcol], iclause);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -643,12 +650,12 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
*/
|
||||
foreach(lc, eclauseset->indexclauses[indexcol])
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
||||
IndexClause *iclause = (IndexClause *) lfirst(lc);
|
||||
|
||||
if (bms_is_subset(rinfo->clause_relids, relids))
|
||||
if (bms_is_subset(iclause->rinfo->clause_relids, relids))
|
||||
{
|
||||
clauseset.indexclauses[indexcol] =
|
||||
lappend(clauseset.indexclauses[indexcol], rinfo);
|
||||
lappend(clauseset.indexclauses[indexcol], iclause);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -688,7 +695,8 @@ eclass_already_used(EquivalenceClass *parent_ec, Relids oldrelids,
|
||||
|
||||
foreach(lc, indexjoinclauses)
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
||||
IndexClause *iclause = (IndexClause *) lfirst(lc);
|
||||
RestrictInfo *rinfo = iclause->rinfo;
|
||||
|
||||
if (rinfo->parent_ec == parent_ec &&
|
||||
bms_is_subset(rinfo->clause_relids, oldrelids))
|
||||
@ -848,7 +856,7 @@ get_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
*
|
||||
* 'rel' is the index's heap relation
|
||||
* 'index' is the index for which we want to generate paths
|
||||
* 'clauses' is the collection of indexable clauses (RestrictInfo nodes)
|
||||
* 'clauses' is the collection of indexable clauses (IndexClause nodes)
|
||||
* 'useful_predicate' indicates whether the index has a useful predicate
|
||||
* 'scantype' indicates whether we need plain or bitmap scan support
|
||||
* 'skip_nonnative_saop' indicates whether to accept SAOP if index AM doesn't
|
||||
@ -865,7 +873,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
List *result = NIL;
|
||||
IndexPath *ipath;
|
||||
List *index_clauses;
|
||||
List *clause_columns;
|
||||
Relids outer_relids;
|
||||
double loop_count;
|
||||
List *orderbyclauses;
|
||||
@ -897,14 +904,12 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Collect the index clauses into a single list.
|
||||
* 1. Combine the per-column IndexClause lists into an overall list.
|
||||
*
|
||||
* We build a list of RestrictInfo nodes for clauses to be used with this
|
||||
* index, along with an integer list of the index column numbers (zero
|
||||
* based) that each clause should be used with. The clauses are ordered
|
||||
* by index key, so that the column numbers form a nondecreasing sequence.
|
||||
* (This order is depended on by btree and possibly other places.) The
|
||||
* lists can be empty, if the index AM allows that.
|
||||
* In the resulting list, clauses are ordered by index key, so that the
|
||||
* column numbers form a nondecreasing sequence. (This order is depended
|
||||
* on by btree and possibly other places.) The list can be empty, if the
|
||||
* index AM allows that.
|
||||
*
|
||||
* found_lower_saop_clause is set true if we accept a ScalarArrayOpExpr
|
||||
* index clause for a non-first index column. This prevents us from
|
||||
@ -918,7 +923,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
* otherwise accounted for.
|
||||
*/
|
||||
index_clauses = NIL;
|
||||
clause_columns = NIL;
|
||||
found_lower_saop_clause = false;
|
||||
outer_relids = bms_copy(rel->lateral_relids);
|
||||
for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
|
||||
@ -927,8 +931,10 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
|
||||
foreach(lc, clauses->indexclauses[indexcol])
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
|
||||
IndexClause *iclause = (IndexClause *) lfirst(lc);
|
||||
RestrictInfo *rinfo = iclause->rinfo;
|
||||
|
||||
/* We might need to omit ScalarArrayOpExpr clauses */
|
||||
if (IsA(rinfo->clause, ScalarArrayOpExpr))
|
||||
{
|
||||
if (!index->amsearcharray)
|
||||
@ -953,8 +959,9 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
found_lower_saop_clause = true;
|
||||
}
|
||||
}
|
||||
index_clauses = lappend(index_clauses, rinfo);
|
||||
clause_columns = lappend_int(clause_columns, indexcol);
|
||||
|
||||
/* OK to include this clause */
|
||||
index_clauses = lappend(index_clauses, iclause);
|
||||
outer_relids = bms_add_members(outer_relids,
|
||||
rinfo->clause_relids);
|
||||
}
|
||||
@ -1036,7 +1043,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
{
|
||||
ipath = create_index_path(root, index,
|
||||
index_clauses,
|
||||
clause_columns,
|
||||
orderbyclauses,
|
||||
orderbyclausecols,
|
||||
useful_pathkeys,
|
||||
@ -1059,7 +1065,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
{
|
||||
ipath = create_index_path(root, index,
|
||||
index_clauses,
|
||||
clause_columns,
|
||||
orderbyclauses,
|
||||
orderbyclausecols,
|
||||
useful_pathkeys,
|
||||
@ -1095,7 +1100,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
{
|
||||
ipath = create_index_path(root, index,
|
||||
index_clauses,
|
||||
clause_columns,
|
||||
NIL,
|
||||
NIL,
|
||||
useful_pathkeys,
|
||||
@ -1113,7 +1117,6 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
|
||||
{
|
||||
ipath = create_index_path(root, index,
|
||||
index_clauses,
|
||||
clause_columns,
|
||||
NIL,
|
||||
NIL,
|
||||
useful_pathkeys,
|
||||
@ -1810,7 +1813,7 @@ get_bitmap_tree_required_outer(Path *bitmapqual)
|
||||
* find_indexpath_quals
|
||||
*
|
||||
* Given the Path structure for a plain or bitmap indexscan, extract lists
|
||||
* of all the indexquals and index predicate conditions used in the Path.
|
||||
* of all the index clauses and index predicate conditions used in the Path.
|
||||
* These are appended to the initial contents of *quals and *preds (hence
|
||||
* caller should initialize those to NIL).
|
||||
*
|
||||
@ -1847,8 +1850,14 @@ find_indexpath_quals(Path *bitmapqual, List **quals, List **preds)
|
||||
else if (IsA(bitmapqual, IndexPath))
|
||||
{
|
||||
IndexPath *ipath = (IndexPath *) bitmapqual;
|
||||
ListCell *l;
|
||||
|
||||
*quals = list_concat(*quals, get_actual_clauses(ipath->indexclauses));
|
||||
foreach(l, ipath->indexclauses)
|
||||
{
|
||||
IndexClause *iclause = (IndexClause *) lfirst(l);
|
||||
|
||||
*quals = lappend(*quals, iclause->rinfo->clause);
|
||||
}
|
||||
*preds = list_concat(*preds, list_copy(ipath->indexinfo->indpred));
|
||||
}
|
||||
else
|
||||
@ -2239,8 +2248,9 @@ match_clauses_to_index(IndexOptInfo *index,
|
||||
* match_clause_to_index
|
||||
* Test whether a qual clause can be used with an index.
|
||||
*
|
||||
* If the clause is usable, add it to the appropriate list in *clauseset.
|
||||
* *clauseset must be initialized to zeroes before first call.
|
||||
* If the clause is usable, add an IndexClause entry for it to the appropriate
|
||||
* list in *clauseset. (*clauseset must be initialized to zeroes before first
|
||||
* call.)
|
||||
*
|
||||
* Note: in some circumstances we may find the same RestrictInfos coming from
|
||||
* multiple places. Defend against redundant outputs by refusing to add a
|
||||
@ -2277,13 +2287,30 @@ match_clause_to_index(IndexOptInfo *index,
|
||||
/* OK, check each index key column for a match */
|
||||
for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
|
||||
{
|
||||
ListCell *lc;
|
||||
|
||||
/* Ignore duplicates */
|
||||
foreach(lc, clauseset->indexclauses[indexcol])
|
||||
{
|
||||
IndexClause *iclause = (IndexClause *) lfirst(lc);
|
||||
|
||||
if (iclause->rinfo == rinfo)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX this should be changed so that we generate an IndexClause
|
||||
* immediately upon matching, to avoid repeated work. To-do soon.
|
||||
*/
|
||||
if (match_clause_to_indexcol(index,
|
||||
indexcol,
|
||||
rinfo))
|
||||
{
|
||||
IndexClause *iclause;
|
||||
|
||||
iclause = expand_indexqual_conditions(index, indexcol, rinfo);
|
||||
clauseset->indexclauses[indexcol] =
|
||||
list_append_unique_ptr(clauseset->indexclauses[indexcol],
|
||||
rinfo);
|
||||
lappend(clauseset->indexclauses[indexcol], iclause);
|
||||
clauseset->nonempty = true;
|
||||
return;
|
||||
}
|
||||
@ -2335,7 +2362,7 @@ match_clause_to_index(IndexOptInfo *index,
|
||||
* target index column. This is sufficient to guarantee that some index
|
||||
* condition can be constructed from the RowCompareExpr --- whether the
|
||||
* remaining columns match the index too is considered in
|
||||
* adjust_rowcompare_for_index().
|
||||
* expand_indexqual_rowcompare().
|
||||
*
|
||||
* It is also possible to match ScalarArrayOpExpr clauses to indexes, when
|
||||
* the clause is of the form "indexkey op ANY (arrayconst)".
|
||||
@ -3342,8 +3369,8 @@ match_index_to_operand(Node *operand,
|
||||
* match_boolean_index_clause() similarly detects clauses that can be
|
||||
* converted into boolean equality operators.
|
||||
*
|
||||
* expand_indexqual_conditions() converts a list of RestrictInfo nodes
|
||||
* (with implicit AND semantics across list elements) into a list of clauses
|
||||
* expand_indexqual_conditions() converts a RestrictInfo node
|
||||
* into an IndexClause, which contains clauses
|
||||
* that the executor can actually handle. For operators that are members of
|
||||
* the index's opfamily this transformation is a no-op, but clauses recognized
|
||||
* by match_special_index_operator() or match_boolean_index_clause() must be
|
||||
@ -3556,38 +3583,28 @@ match_special_index_operator(Expr *clause, Oid opfamily, Oid idxcollation,
|
||||
|
||||
/*
|
||||
* expand_indexqual_conditions
|
||||
* Given a list of RestrictInfo nodes, produce a list of directly usable
|
||||
* index qual clauses.
|
||||
* Given a RestrictInfo node, create an IndexClause.
|
||||
*
|
||||
* Standard qual clauses (those in the index's opfamily) are passed through
|
||||
* unchanged. Boolean clauses and "special" index operators are expanded
|
||||
* into clauses that the indexscan machinery will know what to do with.
|
||||
* RowCompare clauses are simplified if necessary to create a clause that is
|
||||
* fully checkable by the index.
|
||||
*
|
||||
* In addition to the expressions themselves, there are auxiliary lists
|
||||
* of the index column numbers that the clauses are meant to be used with;
|
||||
* we generate an updated column number list for the result. (This is not
|
||||
* the identical list because one input clause sometimes produces more than
|
||||
* one output clause.)
|
||||
*
|
||||
* The input clauses are sorted by column number, and so the output is too.
|
||||
* (This is depended on in various places in both planner and executor.)
|
||||
*/
|
||||
void
|
||||
static IndexClause *
|
||||
expand_indexqual_conditions(IndexOptInfo *index,
|
||||
List *indexclauses, List *indexclausecols,
|
||||
List **indexquals_p, List **indexqualcols_p)
|
||||
int indexcol,
|
||||
RestrictInfo *rinfo)
|
||||
{
|
||||
IndexClause *iclause = makeNode(IndexClause);
|
||||
List *indexquals = NIL;
|
||||
List *indexqualcols = NIL;
|
||||
ListCell *lcc,
|
||||
*lci;
|
||||
|
||||
forboth(lcc, indexclauses, lci, indexclausecols)
|
||||
iclause->rinfo = rinfo;
|
||||
iclause->lossy = false; /* might get changed below */
|
||||
iclause->indexcol = indexcol;
|
||||
iclause->indexcols = NIL; /* might get changed below */
|
||||
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lcc);
|
||||
int indexcol = lfirst_int(lci);
|
||||
Expr *clause = rinfo->clause;
|
||||
Oid curFamily;
|
||||
Oid curCollation;
|
||||
@ -3607,10 +3624,9 @@ expand_indexqual_conditions(IndexOptInfo *index,
|
||||
index);
|
||||
if (boolqual)
|
||||
{
|
||||
indexquals = lappend(indexquals,
|
||||
make_simple_restrictinfo(boolqual));
|
||||
indexqualcols = lappend_int(indexqualcols, indexcol);
|
||||
continue;
|
||||
iclause->indexquals =
|
||||
list_make1(make_simple_restrictinfo(boolqual));
|
||||
return iclause;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3620,41 +3636,63 @@ expand_indexqual_conditions(IndexOptInfo *index,
|
||||
*/
|
||||
if (is_opclause(clause))
|
||||
{
|
||||
indexquals = list_concat(indexquals,
|
||||
expand_indexqual_opclause(rinfo,
|
||||
curFamily,
|
||||
curCollation));
|
||||
/* expand_indexqual_opclause can produce multiple clauses */
|
||||
while (list_length(indexqualcols) < list_length(indexquals))
|
||||
indexqualcols = lappend_int(indexqualcols, indexcol);
|
||||
/*
|
||||
* Check to see if the indexkey is on the right; if so, commute
|
||||
* the clause. The indexkey should be the side that refers to
|
||||
* (only) the base relation.
|
||||
*/
|
||||
if (!bms_equal(rinfo->left_relids, index->rel->relids))
|
||||
{
|
||||
Oid opno = ((OpExpr *) clause)->opno;
|
||||
RestrictInfo *newrinfo;
|
||||
|
||||
newrinfo = commute_restrictinfo(rinfo,
|
||||
get_commutator(opno));
|
||||
|
||||
/*
|
||||
* For now, assume it couldn't be any case that requires
|
||||
* expansion. (This is OK for the current capabilities of
|
||||
* expand_indexqual_opclause, but we'll need to remove the
|
||||
* restriction when we open this up for extensions.)
|
||||
*/
|
||||
indexquals = list_make1(newrinfo);
|
||||
}
|
||||
else
|
||||
indexquals = expand_indexqual_opclause(rinfo,
|
||||
curFamily,
|
||||
curCollation,
|
||||
&iclause->lossy);
|
||||
}
|
||||
else if (IsA(clause, ScalarArrayOpExpr))
|
||||
{
|
||||
/* no extra work at this time */
|
||||
indexquals = lappend(indexquals, rinfo);
|
||||
indexqualcols = lappend_int(indexqualcols, indexcol);
|
||||
}
|
||||
else if (IsA(clause, RowCompareExpr))
|
||||
{
|
||||
indexquals = lappend(indexquals,
|
||||
expand_indexqual_rowcompare(rinfo,
|
||||
index,
|
||||
indexcol));
|
||||
indexqualcols = lappend_int(indexqualcols, indexcol);
|
||||
RestrictInfo *newrinfo;
|
||||
|
||||
newrinfo = expand_indexqual_rowcompare(rinfo,
|
||||
index,
|
||||
indexcol,
|
||||
&iclause->indexcols,
|
||||
&iclause->lossy);
|
||||
if (newrinfo != rinfo)
|
||||
{
|
||||
/* We need to report a derived expression */
|
||||
indexquals = list_make1(newrinfo);
|
||||
}
|
||||
}
|
||||
else if (IsA(clause, NullTest))
|
||||
{
|
||||
Assert(index->amsearchnulls);
|
||||
indexquals = lappend(indexquals, rinfo);
|
||||
indexqualcols = lappend_int(indexqualcols, indexcol);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "unsupported indexqual type: %d",
|
||||
(int) nodeTag(clause));
|
||||
}
|
||||
|
||||
*indexquals_p = indexquals;
|
||||
*indexqualcols_p = indexqualcols;
|
||||
iclause->indexquals = indexquals;
|
||||
return iclause;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3725,13 +3763,15 @@ expand_boolean_index_clause(Node *clause,
|
||||
* expand_indexqual_opclause --- expand a single indexqual condition
|
||||
* that is an operator clause
|
||||
*
|
||||
* The input is a single RestrictInfo, the output a list of RestrictInfos.
|
||||
* The input is a single RestrictInfo, the output a list of RestrictInfos,
|
||||
* or NIL if the RestrictInfo's clause can be used as-is.
|
||||
*
|
||||
* In the base case this is just list_make1(), but we have to be prepared to
|
||||
* In the base case this is just "return NIL", but we have to be prepared to
|
||||
* expand special cases that were accepted by match_special_index_operator().
|
||||
*/
|
||||
static List *
|
||||
expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
|
||||
expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation,
|
||||
bool *lossy)
|
||||
{
|
||||
Expr *clause = rinfo->clause;
|
||||
|
||||
@ -3760,6 +3800,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
|
||||
case OID_BYTEA_LIKE_OP:
|
||||
if (!op_in_opfamily(expr_op, opfamily))
|
||||
{
|
||||
*lossy = true;
|
||||
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, expr_coll,
|
||||
&prefix, NULL);
|
||||
return prefix_quals(leftop, opfamily, idxcollation, prefix, pstatus);
|
||||
@ -3771,6 +3812,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
|
||||
case OID_NAME_ICLIKE_OP:
|
||||
if (!op_in_opfamily(expr_op, opfamily))
|
||||
{
|
||||
*lossy = true;
|
||||
/* the right-hand const is type text for all of these */
|
||||
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, expr_coll,
|
||||
&prefix, NULL);
|
||||
@ -3783,6 +3825,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
|
||||
case OID_NAME_REGEXEQ_OP:
|
||||
if (!op_in_opfamily(expr_op, opfamily))
|
||||
{
|
||||
*lossy = true;
|
||||
/* the right-hand const is type text for all of these */
|
||||
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex, expr_coll,
|
||||
&prefix, NULL);
|
||||
@ -3795,6 +3838,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
|
||||
case OID_NAME_ICREGEXEQ_OP:
|
||||
if (!op_in_opfamily(expr_op, opfamily))
|
||||
{
|
||||
*lossy = true;
|
||||
/* the right-hand const is type text for all of these */
|
||||
pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, expr_coll,
|
||||
&prefix, NULL);
|
||||
@ -3806,96 +3850,65 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid idxcollation)
|
||||
case OID_INET_SUBEQ_OP:
|
||||
if (!op_in_opfamily(expr_op, opfamily))
|
||||
{
|
||||
*lossy = true;
|
||||
return network_prefix_quals(leftop, expr_op, opfamily,
|
||||
patt->constvalue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Default case: just make a list of the unmodified indexqual */
|
||||
return list_make1(rinfo);
|
||||
/* Default case: the clause can be used as-is. */
|
||||
*lossy = false;
|
||||
return NIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* expand_indexqual_rowcompare --- expand a single indexqual condition
|
||||
* that is a RowCompareExpr
|
||||
*
|
||||
* This is a thin wrapper around adjust_rowcompare_for_index; we export the
|
||||
* latter so that createplan.c can use it to re-discover which columns of the
|
||||
* index are used by a row comparison indexqual.
|
||||
*/
|
||||
static RestrictInfo *
|
||||
expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
IndexOptInfo *index,
|
||||
int indexcol)
|
||||
{
|
||||
RowCompareExpr *clause = (RowCompareExpr *) rinfo->clause;
|
||||
Expr *newclause;
|
||||
List *indexcolnos;
|
||||
bool var_on_left;
|
||||
|
||||
newclause = adjust_rowcompare_for_index(clause,
|
||||
index,
|
||||
indexcol,
|
||||
&indexcolnos,
|
||||
&var_on_left);
|
||||
|
||||
/*
|
||||
* If we didn't have to change the RowCompareExpr, return the original
|
||||
* RestrictInfo.
|
||||
*/
|
||||
if (newclause == (Expr *) clause)
|
||||
return rinfo;
|
||||
|
||||
/* Else we need a new RestrictInfo */
|
||||
return make_simple_restrictinfo(newclause);
|
||||
}
|
||||
|
||||
/*
|
||||
* adjust_rowcompare_for_index --- expand a single indexqual condition
|
||||
* that is a RowCompareExpr
|
||||
*
|
||||
* It's already known that the first column of the row comparison matches
|
||||
* the specified column of the index. We can use additional columns of the
|
||||
* row comparison as index qualifications, so long as they match the index
|
||||
* in the "same direction", ie, the indexkeys are all on the same side of the
|
||||
* clause and the operators are all the same-type members of the opfamilies.
|
||||
*
|
||||
* If all the columns of the RowCompareExpr match in this way, we just use it
|
||||
* as-is. Otherwise, we build a shortened RowCompareExpr (if more than one
|
||||
* as-is, except for possibly commuting it to put the indexkeys on the left.
|
||||
*
|
||||
* Otherwise, we build a shortened RowCompareExpr (if more than one
|
||||
* column matches) or a simple OpExpr (if the first-column match is all
|
||||
* there is). In these cases the modified clause is always "<=" or ">="
|
||||
* even when the original was "<" or ">" --- this is necessary to match all
|
||||
* the rows that could match the original. (We are essentially building a
|
||||
* lossy version of the row comparison when we do this.)
|
||||
* the rows that could match the original. (We are building a lossy version
|
||||
* of the row comparison when we do this, so we set *lossy = true.)
|
||||
*
|
||||
* *indexcolnos receives an integer list of the index column numbers (zero
|
||||
* based) used in the resulting expression. The reason we need to return
|
||||
* that is that if the index is selected for use, createplan.c will need to
|
||||
* call this again to extract that list. (This is a bit grotty, but row
|
||||
* comparison indexquals aren't used enough to justify finding someplace to
|
||||
* keep the information in the Path representation.) Since createplan.c
|
||||
* also needs to know which side of the RowCompareExpr is the index side,
|
||||
* we also return *var_on_left_p rather than re-deducing that there.
|
||||
* based) used in the resulting expression. We have to pass that back
|
||||
* because createplan.c will need it.
|
||||
*/
|
||||
Expr *
|
||||
adjust_rowcompare_for_index(RowCompareExpr *clause,
|
||||
static RestrictInfo *
|
||||
expand_indexqual_rowcompare(RestrictInfo *rinfo,
|
||||
IndexOptInfo *index,
|
||||
int indexcol,
|
||||
List **indexcolnos,
|
||||
bool *var_on_left_p)
|
||||
bool *lossy)
|
||||
{
|
||||
RowCompareExpr *clause = castNode(RowCompareExpr, rinfo->clause);
|
||||
bool var_on_left;
|
||||
int op_strategy;
|
||||
Oid op_lefttype;
|
||||
Oid op_righttype;
|
||||
int matching_cols;
|
||||
Oid expr_op;
|
||||
List *expr_ops;
|
||||
List *opfamilies;
|
||||
List *lefttypes;
|
||||
List *righttypes;
|
||||
List *new_ops;
|
||||
ListCell *largs_cell;
|
||||
ListCell *rargs_cell;
|
||||
List *var_args;
|
||||
List *non_var_args;
|
||||
ListCell *vargs_cell;
|
||||
ListCell *nargs_cell;
|
||||
ListCell *opnos_cell;
|
||||
ListCell *collids_cell;
|
||||
|
||||
@ -3905,7 +3918,17 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
|
||||
Assert(var_on_left ||
|
||||
match_index_to_operand((Node *) linitial(clause->rargs),
|
||||
indexcol, index));
|
||||
*var_on_left_p = var_on_left;
|
||||
|
||||
if (var_on_left)
|
||||
{
|
||||
var_args = clause->largs;
|
||||
non_var_args = clause->rargs;
|
||||
}
|
||||
else
|
||||
{
|
||||
var_args = clause->rargs;
|
||||
non_var_args = clause->largs;
|
||||
}
|
||||
|
||||
expr_op = linitial_oid(clause->opnos);
|
||||
if (!var_on_left)
|
||||
@ -3918,7 +3941,8 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
|
||||
/* Initialize returned list of which index columns are used */
|
||||
*indexcolnos = list_make1_int(indexcol);
|
||||
|
||||
/* Build lists of the opfamilies and operator datatypes in case needed */
|
||||
/* Build lists of ops, opfamilies and operator datatypes in case needed */
|
||||
expr_ops = list_make1_oid(expr_op);
|
||||
opfamilies = list_make1_oid(index->opfamily[indexcol]);
|
||||
lefttypes = list_make1_oid(op_lefttype);
|
||||
righttypes = list_make1_oid(op_righttype);
|
||||
@ -3930,27 +3954,20 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
|
||||
* indexed relation.
|
||||
*/
|
||||
matching_cols = 1;
|
||||
largs_cell = lnext(list_head(clause->largs));
|
||||
rargs_cell = lnext(list_head(clause->rargs));
|
||||
vargs_cell = lnext(list_head(var_args));
|
||||
nargs_cell = lnext(list_head(non_var_args));
|
||||
opnos_cell = lnext(list_head(clause->opnos));
|
||||
collids_cell = lnext(list_head(clause->inputcollids));
|
||||
|
||||
while (largs_cell != NULL)
|
||||
while (vargs_cell != NULL)
|
||||
{
|
||||
Node *varop;
|
||||
Node *constop;
|
||||
Node *varop = (Node *) lfirst(vargs_cell);
|
||||
Node *constop = (Node *) lfirst(nargs_cell);
|
||||
int i;
|
||||
|
||||
expr_op = lfirst_oid(opnos_cell);
|
||||
if (var_on_left)
|
||||
if (!var_on_left)
|
||||
{
|
||||
varop = (Node *) lfirst(largs_cell);
|
||||
constop = (Node *) lfirst(rargs_cell);
|
||||
}
|
||||
else
|
||||
{
|
||||
varop = (Node *) lfirst(rargs_cell);
|
||||
constop = (Node *) lfirst(largs_cell);
|
||||
/* indexkey is on right, so commute the operator */
|
||||
expr_op = get_commutator(expr_op);
|
||||
if (expr_op == InvalidOid)
|
||||
@ -3980,37 +3997,49 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
|
||||
/* Add column number to returned list */
|
||||
*indexcolnos = lappend_int(*indexcolnos, i);
|
||||
|
||||
/* Add opfamily and datatypes to lists */
|
||||
/* Add operator info to lists */
|
||||
get_op_opfamily_properties(expr_op, index->opfamily[i], false,
|
||||
&op_strategy,
|
||||
&op_lefttype,
|
||||
&op_righttype);
|
||||
expr_ops = lappend_oid(expr_ops, expr_op);
|
||||
opfamilies = lappend_oid(opfamilies, index->opfamily[i]);
|
||||
lefttypes = lappend_oid(lefttypes, op_lefttype);
|
||||
righttypes = lappend_oid(righttypes, op_righttype);
|
||||
|
||||
/* This column matches, keep scanning */
|
||||
matching_cols++;
|
||||
largs_cell = lnext(largs_cell);
|
||||
rargs_cell = lnext(rargs_cell);
|
||||
vargs_cell = lnext(vargs_cell);
|
||||
nargs_cell = lnext(nargs_cell);
|
||||
opnos_cell = lnext(opnos_cell);
|
||||
collids_cell = lnext(collids_cell);
|
||||
}
|
||||
|
||||
/* Return clause as-is if it's all usable as index quals */
|
||||
if (matching_cols == list_length(clause->opnos))
|
||||
return (Expr *) clause;
|
||||
/* Result is non-lossy if all columns are usable as index quals */
|
||||
*lossy = (matching_cols != list_length(clause->opnos));
|
||||
|
||||
/*
|
||||
* We have to generate a subset rowcompare (possibly just one OpExpr). The
|
||||
* painful part of this is changing < to <= or > to >=, so deal with that
|
||||
* first.
|
||||
* Return clause as-is if we have var on left and it's all usable as index
|
||||
* quals
|
||||
*/
|
||||
if (op_strategy == BTLessEqualStrategyNumber ||
|
||||
op_strategy == BTGreaterEqualStrategyNumber)
|
||||
if (var_on_left && !*lossy)
|
||||
return rinfo;
|
||||
|
||||
/*
|
||||
* We have to generate a modified rowcompare (possibly just one OpExpr).
|
||||
* The painful part of this is changing < to <= or > to >=, so deal with
|
||||
* that first.
|
||||
*/
|
||||
if (!*lossy)
|
||||
{
|
||||
/* easy, just use the same operators */
|
||||
new_ops = list_truncate(list_copy(clause->opnos), matching_cols);
|
||||
/* very easy, just use the commuted operators */
|
||||
new_ops = expr_ops;
|
||||
}
|
||||
else if (op_strategy == BTLessEqualStrategyNumber ||
|
||||
op_strategy == BTGreaterEqualStrategyNumber)
|
||||
{
|
||||
/* easy, just use the same (possibly commuted) operators */
|
||||
new_ops = list_truncate(expr_ops, matching_cols);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -4025,9 +4054,9 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
|
||||
else
|
||||
elog(ERROR, "unexpected strategy number %d", op_strategy);
|
||||
new_ops = NIL;
|
||||
lefttypes_cell = list_head(lefttypes);
|
||||
righttypes_cell = list_head(righttypes);
|
||||
foreach(opfamilies_cell, opfamilies)
|
||||
forthree(opfamilies_cell, opfamilies,
|
||||
lefttypes_cell, lefttypes,
|
||||
righttypes_cell, righttypes)
|
||||
{
|
||||
Oid opfam = lfirst_oid(opfamilies_cell);
|
||||
Oid lefttype = lfirst_oid(lefttypes_cell);
|
||||
@ -4038,16 +4067,7 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
|
||||
if (!OidIsValid(expr_op)) /* should not happen */
|
||||
elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
|
||||
op_strategy, lefttype, righttype, opfam);
|
||||
if (!var_on_left)
|
||||
{
|
||||
expr_op = get_commutator(expr_op);
|
||||
if (!OidIsValid(expr_op)) /* should not happen */
|
||||
elog(ERROR, "could not find commutator of operator %d(%u,%u) of opfamily %u",
|
||||
op_strategy, lefttype, righttype, opfam);
|
||||
}
|
||||
new_ops = lappend_oid(new_ops, expr_op);
|
||||
lefttypes_cell = lnext(lefttypes_cell);
|
||||
righttypes_cell = lnext(righttypes_cell);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4056,29 +4076,31 @@ adjust_rowcompare_for_index(RowCompareExpr *clause,
|
||||
{
|
||||
RowCompareExpr *rc = makeNode(RowCompareExpr);
|
||||
|
||||
if (var_on_left)
|
||||
rc->rctype = (RowCompareType) op_strategy;
|
||||
else
|
||||
rc->rctype = (op_strategy == BTLessEqualStrategyNumber) ?
|
||||
ROWCOMPARE_GE : ROWCOMPARE_LE;
|
||||
rc->rctype = (RowCompareType) op_strategy;
|
||||
rc->opnos = new_ops;
|
||||
rc->opfamilies = list_truncate(list_copy(clause->opfamilies),
|
||||
matching_cols);
|
||||
rc->inputcollids = list_truncate(list_copy(clause->inputcollids),
|
||||
matching_cols);
|
||||
rc->largs = list_truncate(copyObject(clause->largs),
|
||||
rc->largs = list_truncate(copyObject(var_args),
|
||||
matching_cols);
|
||||
rc->rargs = list_truncate(copyObject(clause->rargs),
|
||||
rc->rargs = list_truncate(copyObject(non_var_args),
|
||||
matching_cols);
|
||||
return (Expr *) rc;
|
||||
return make_simple_restrictinfo((Expr *) rc);
|
||||
}
|
||||
else
|
||||
{
|
||||
return make_opclause(linitial_oid(new_ops), BOOLOID, false,
|
||||
copyObject(linitial(clause->largs)),
|
||||
copyObject(linitial(clause->rargs)),
|
||||
InvalidOid,
|
||||
linitial_oid(clause->inputcollids));
|
||||
Expr *op;
|
||||
|
||||
/* We don't report an index column list in this case */
|
||||
*indexcolnos = NIL;
|
||||
|
||||
op = make_opclause(linitial_oid(new_ops), BOOLOID, false,
|
||||
copyObject(linitial(var_args)),
|
||||
copyObject(linitial(non_var_args)),
|
||||
InvalidOid,
|
||||
linitial_oid(clause->inputcollids));
|
||||
return make_simple_restrictinfo(op);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,8 +152,13 @@ static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path)
|
||||
static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path);
|
||||
static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
|
||||
static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
|
||||
static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
|
||||
static void fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
|
||||
List **stripped_indexquals_p,
|
||||
List **fixed_indexquals_p);
|
||||
static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
|
||||
static Node *fix_indexqual_clause(PlannerInfo *root,
|
||||
IndexOptInfo *index, int indexcol,
|
||||
Node *clause, List *indexcolnos);
|
||||
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
|
||||
static List *get_switched_clauses(List *clauses, Relids outerrelids);
|
||||
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
|
||||
@ -2607,7 +2612,7 @@ create_indexscan_plan(PlannerInfo *root,
|
||||
bool indexonly)
|
||||
{
|
||||
Scan *scan_plan;
|
||||
List *indexquals = best_path->indexquals;
|
||||
List *indexclauses = best_path->indexclauses;
|
||||
List *indexorderbys = best_path->indexorderbys;
|
||||
Index baserelid = best_path->path.parent->relid;
|
||||
Oid indexoid = best_path->indexinfo->indexoid;
|
||||
@ -2623,16 +2628,14 @@ create_indexscan_plan(PlannerInfo *root,
|
||||
Assert(best_path->path.parent->rtekind == RTE_RELATION);
|
||||
|
||||
/*
|
||||
* Build "stripped" indexquals structure (no RestrictInfos) to pass to
|
||||
* executor as indexqualorig
|
||||
* Extract the index qual expressions (stripped of RestrictInfos) from the
|
||||
* IndexClauses list, and prepare a copy with index Vars substituted for
|
||||
* table Vars. (This step also does replace_nestloop_params on the
|
||||
* fixed_indexquals.)
|
||||
*/
|
||||
stripped_indexquals = get_actual_clauses(indexquals);
|
||||
|
||||
/*
|
||||
* The executor needs a copy with the indexkey on the left of each clause
|
||||
* and with index Vars substituted for table ones.
|
||||
*/
|
||||
fixed_indexquals = fix_indexqual_references(root, best_path);
|
||||
fix_indexqual_references(root, best_path,
|
||||
&stripped_indexquals,
|
||||
&fixed_indexquals);
|
||||
|
||||
/*
|
||||
* Likewise fix up index attr references in the ORDER BY expressions.
|
||||
@ -2648,14 +2651,14 @@ create_indexscan_plan(PlannerInfo *root,
|
||||
* included in qpqual. The upshot is that qpqual must contain
|
||||
* scan_clauses minus whatever appears in indexquals.
|
||||
*
|
||||
* In normal cases simple pointer equality checks will be enough to spot
|
||||
* duplicate RestrictInfos, so we try that first.
|
||||
*
|
||||
* Another common case is that a scan_clauses entry is generated from the
|
||||
* same EquivalenceClass as some indexqual, and is therefore redundant
|
||||
* with it, though not equal. (This happens when indxpath.c prefers a
|
||||
* is_redundant_with_indexclauses() detects cases where a scan clause is
|
||||
* present in the indexclauses list or is generated from the same
|
||||
* EquivalenceClass as some indexclause, and is therefore redundant with
|
||||
* it, though not equal. (The latter happens when indxpath.c prefers a
|
||||
* different derived equality than what generate_join_implied_equalities
|
||||
* picked for a parameterized scan's ppi_clauses.)
|
||||
* picked for a parameterized scan's ppi_clauses.) Note that it will not
|
||||
* match to lossy index clauses, which is critical because we have to
|
||||
* include the original clause in qpqual in that case.
|
||||
*
|
||||
* In some situations (particularly with OR'd index conditions) we may
|
||||
* have scan_clauses that are not equal to, but are logically implied by,
|
||||
@ -2674,12 +2677,11 @@ create_indexscan_plan(PlannerInfo *root,
|
||||
|
||||
if (rinfo->pseudoconstant)
|
||||
continue; /* we may drop pseudoconstants here */
|
||||
if (list_member_ptr(indexquals, rinfo))
|
||||
continue; /* simple duplicate */
|
||||
if (is_redundant_derived_clause(rinfo, indexquals))
|
||||
continue; /* derived from same EquivalenceClass */
|
||||
if (is_redundant_with_indexclauses(rinfo, indexclauses))
|
||||
continue; /* dup or derived from same EquivalenceClass */
|
||||
if (!contain_mutable_functions((Node *) rinfo->clause) &&
|
||||
predicate_implied_by(list_make1(rinfo->clause), indexquals, false))
|
||||
predicate_implied_by(list_make1(rinfo->clause), stripped_indexquals,
|
||||
false))
|
||||
continue; /* provably implied by indexquals */
|
||||
qpqual = lappend(qpqual, rinfo);
|
||||
}
|
||||
@ -3040,6 +3042,8 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
|
||||
{
|
||||
IndexPath *ipath = (IndexPath *) bitmapqual;
|
||||
IndexScan *iscan;
|
||||
List *subquals;
|
||||
List *subindexquals;
|
||||
List *subindexECs;
|
||||
ListCell *l;
|
||||
|
||||
@ -3060,8 +3064,26 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
|
||||
plan->plan_width = 0; /* meaningless */
|
||||
plan->parallel_aware = false;
|
||||
plan->parallel_safe = ipath->path.parallel_safe;
|
||||
*qual = get_actual_clauses(ipath->indexclauses);
|
||||
*indexqual = get_actual_clauses(ipath->indexquals);
|
||||
/* Extract original index clauses, actual index quals, relevant ECs */
|
||||
subquals = NIL;
|
||||
subindexquals = NIL;
|
||||
subindexECs = NIL;
|
||||
foreach(l, ipath->indexclauses)
|
||||
{
|
||||
IndexClause *iclause = (IndexClause *) lfirst(l);
|
||||
RestrictInfo *rinfo = iclause->rinfo;
|
||||
|
||||
Assert(!rinfo->pseudoconstant);
|
||||
subquals = lappend(subquals, rinfo->clause);
|
||||
if (iclause->indexquals)
|
||||
subindexquals = list_concat(subindexquals,
|
||||
get_actual_clauses(iclause->indexquals));
|
||||
else
|
||||
subindexquals = lappend(subindexquals, rinfo->clause);
|
||||
if (rinfo->parent_ec)
|
||||
subindexECs = lappend(subindexECs, rinfo->parent_ec);
|
||||
}
|
||||
/* We can add any index predicate conditions, too */
|
||||
foreach(l, ipath->indexinfo->indpred)
|
||||
{
|
||||
Expr *pred = (Expr *) lfirst(l);
|
||||
@ -3072,21 +3094,14 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
|
||||
* the conditions that got pushed into the bitmapqual. Avoid
|
||||
* generating redundant conditions.
|
||||
*/
|
||||
if (!predicate_implied_by(list_make1(pred), ipath->indexclauses,
|
||||
false))
|
||||
if (!predicate_implied_by(list_make1(pred), subquals, false))
|
||||
{
|
||||
*qual = lappend(*qual, pred);
|
||||
*indexqual = lappend(*indexqual, pred);
|
||||
subquals = lappend(subquals, pred);
|
||||
subindexquals = lappend(subindexquals, pred);
|
||||
}
|
||||
}
|
||||
subindexECs = NIL;
|
||||
foreach(l, ipath->indexquals)
|
||||
{
|
||||
RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
|
||||
|
||||
if (rinfo->parent_ec)
|
||||
subindexECs = lappend(subindexECs, rinfo->parent_ec);
|
||||
}
|
||||
*qual = subquals;
|
||||
*indexqual = subindexquals;
|
||||
*indexECs = subindexECs;
|
||||
}
|
||||
else
|
||||
@ -4446,138 +4461,67 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
|
||||
* Adjust indexqual clauses to the form the executor's indexqual
|
||||
* machinery needs.
|
||||
*
|
||||
* We have four tasks here:
|
||||
* * Remove RestrictInfo nodes from the input clauses.
|
||||
* We have three tasks here:
|
||||
* * Select the actual qual clauses out of the input IndexClause list,
|
||||
* and remove RestrictInfo nodes from the qual clauses.
|
||||
* * Replace any outer-relation Var or PHV nodes with nestloop Params.
|
||||
* (XXX eventually, that responsibility should go elsewhere?)
|
||||
* * Index keys must be represented by Var nodes with varattno set to the
|
||||
* index's attribute number, not the attribute number in the original rel.
|
||||
* * If the index key is on the right, commute the clause to put it on the
|
||||
* left.
|
||||
*
|
||||
* The result is a modified copy of the path's indexquals list --- the
|
||||
* original is not changed. Note also that the copy shares no substructure
|
||||
* with the original; this is needed in case there is a subplan in it (we need
|
||||
* two separate copies of the subplan tree, or things will go awry).
|
||||
* *stripped_indexquals_p receives a list of the actual qual clauses.
|
||||
*
|
||||
* *fixed_indexquals_p receives a list of the adjusted quals. This is a copy
|
||||
* that shares no substructure with the original; this is needed in case there
|
||||
* are subplans in it (we need two separate copies of the subplan tree, or
|
||||
* things will go awry).
|
||||
*/
|
||||
static List *
|
||||
fix_indexqual_references(PlannerInfo *root, IndexPath *index_path)
|
||||
static void
|
||||
fix_indexqual_references(PlannerInfo *root, IndexPath *index_path,
|
||||
List **stripped_indexquals_p, List **fixed_indexquals_p)
|
||||
{
|
||||
IndexOptInfo *index = index_path->indexinfo;
|
||||
List *stripped_indexquals;
|
||||
List *fixed_indexquals;
|
||||
ListCell *lcc,
|
||||
*lci;
|
||||
ListCell *lc;
|
||||
|
||||
fixed_indexquals = NIL;
|
||||
stripped_indexquals = fixed_indexquals = NIL;
|
||||
|
||||
forboth(lcc, index_path->indexquals, lci, index_path->indexqualcols)
|
||||
foreach(lc, index_path->indexclauses)
|
||||
{
|
||||
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcc);
|
||||
int indexcol = lfirst_int(lci);
|
||||
Node *clause;
|
||||
IndexClause *iclause = lfirst_node(IndexClause, lc);
|
||||
int indexcol = iclause->indexcol;
|
||||
|
||||
/*
|
||||
* Replace any outer-relation variables with nestloop params.
|
||||
*
|
||||
* This also makes a copy of the clause, so it's safe to modify it
|
||||
* in-place below.
|
||||
*/
|
||||
clause = replace_nestloop_params(root, (Node *) rinfo->clause);
|
||||
|
||||
if (IsA(clause, OpExpr))
|
||||
if (iclause->indexquals == NIL)
|
||||
{
|
||||
OpExpr *op = (OpExpr *) clause;
|
||||
/* rinfo->clause is directly usable as an indexqual */
|
||||
Node *clause = (Node *) iclause->rinfo->clause;
|
||||
|
||||
if (list_length(op->args) != 2)
|
||||
elog(ERROR, "indexqual clause is not binary opclause");
|
||||
|
||||
/*
|
||||
* Check to see if the indexkey is on the right; if so, commute
|
||||
* the clause. The indexkey should be the side that refers to
|
||||
* (only) the base relation.
|
||||
*/
|
||||
if (!bms_equal(rinfo->left_relids, index->rel->relids))
|
||||
CommuteOpExpr(op);
|
||||
|
||||
/*
|
||||
* Now replace the indexkey expression with an index Var.
|
||||
*/
|
||||
linitial(op->args) = fix_indexqual_operand(linitial(op->args),
|
||||
index,
|
||||
indexcol);
|
||||
}
|
||||
else if (IsA(clause, RowCompareExpr))
|
||||
{
|
||||
RowCompareExpr *rc = (RowCompareExpr *) clause;
|
||||
Expr *newrc;
|
||||
List *indexcolnos;
|
||||
bool var_on_left;
|
||||
ListCell *lca,
|
||||
*lcai;
|
||||
|
||||
/*
|
||||
* Re-discover which index columns are used in the rowcompare.
|
||||
*/
|
||||
newrc = adjust_rowcompare_for_index(rc,
|
||||
index,
|
||||
indexcol,
|
||||
&indexcolnos,
|
||||
&var_on_left);
|
||||
|
||||
/*
|
||||
* Trouble if adjust_rowcompare_for_index thought the
|
||||
* RowCompareExpr didn't match the index as-is; the clause should
|
||||
* have gone through that routine already.
|
||||
*/
|
||||
if (newrc != (Expr *) rc)
|
||||
elog(ERROR, "inconsistent results from adjust_rowcompare_for_index");
|
||||
|
||||
/*
|
||||
* Check to see if the indexkey is on the right; if so, commute
|
||||
* the clause.
|
||||
*/
|
||||
if (!var_on_left)
|
||||
CommuteRowCompareExpr(rc);
|
||||
|
||||
/*
|
||||
* Now replace the indexkey expressions with index Vars.
|
||||
*/
|
||||
Assert(list_length(rc->largs) == list_length(indexcolnos));
|
||||
forboth(lca, rc->largs, lcai, indexcolnos)
|
||||
{
|
||||
lfirst(lca) = fix_indexqual_operand(lfirst(lca),
|
||||
index,
|
||||
lfirst_int(lcai));
|
||||
}
|
||||
}
|
||||
else if (IsA(clause, ScalarArrayOpExpr))
|
||||
{
|
||||
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
|
||||
|
||||
/* Never need to commute... */
|
||||
|
||||
/* Replace the indexkey expression with an index Var. */
|
||||
linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
|
||||
index,
|
||||
indexcol);
|
||||
}
|
||||
else if (IsA(clause, NullTest))
|
||||
{
|
||||
NullTest *nt = (NullTest *) clause;
|
||||
|
||||
/* Replace the indexkey expression with an index Var. */
|
||||
nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg,
|
||||
index,
|
||||
indexcol);
|
||||
stripped_indexquals = lappend(stripped_indexquals, clause);
|
||||
clause = fix_indexqual_clause(root, index, indexcol,
|
||||
clause, iclause->indexcols);
|
||||
fixed_indexquals = lappend(fixed_indexquals, clause);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "unsupported indexqual type: %d",
|
||||
(int) nodeTag(clause));
|
||||
{
|
||||
/* Process the derived indexquals */
|
||||
ListCell *lc2;
|
||||
|
||||
fixed_indexquals = lappend(fixed_indexquals, clause);
|
||||
foreach(lc2, iclause->indexquals)
|
||||
{
|
||||
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
|
||||
Node *clause = (Node *) rinfo->clause;
|
||||
|
||||
stripped_indexquals = lappend(stripped_indexquals, clause);
|
||||
clause = fix_indexqual_clause(root, index, indexcol,
|
||||
clause, iclause->indexcols);
|
||||
fixed_indexquals = lappend(fixed_indexquals, clause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fixed_indexquals;
|
||||
*stripped_indexquals_p = stripped_indexquals;
|
||||
*fixed_indexquals_p = fixed_indexquals;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4585,11 +4529,8 @@ fix_indexqual_references(PlannerInfo *root, IndexPath *index_path)
|
||||
* Adjust indexorderby clauses to the form the executor's index
|
||||
* machinery needs.
|
||||
*
|
||||
* This is a simplified version of fix_indexqual_references. The input does
|
||||
* not have RestrictInfo nodes, and we assume that indxpath.c already
|
||||
* commuted the clauses to put the index keys on the left. Also, we don't
|
||||
* bother to support any cases except simple OpExprs, since nothing else
|
||||
* is allowed for ordering operators.
|
||||
* This is a simplified version of fix_indexqual_references. The input is
|
||||
* bare clauses and a separate indexcol list, instead of IndexClauses.
|
||||
*/
|
||||
static List *
|
||||
fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path)
|
||||
@ -4606,38 +4547,81 @@ fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path)
|
||||
Node *clause = (Node *) lfirst(lcc);
|
||||
int indexcol = lfirst_int(lci);
|
||||
|
||||
/*
|
||||
* Replace any outer-relation variables with nestloop params.
|
||||
*
|
||||
* This also makes a copy of the clause, so it's safe to modify it
|
||||
* in-place below.
|
||||
*/
|
||||
clause = replace_nestloop_params(root, clause);
|
||||
|
||||
if (IsA(clause, OpExpr))
|
||||
{
|
||||
OpExpr *op = (OpExpr *) clause;
|
||||
|
||||
if (list_length(op->args) != 2)
|
||||
elog(ERROR, "indexorderby clause is not binary opclause");
|
||||
|
||||
/*
|
||||
* Now replace the indexkey expression with an index Var.
|
||||
*/
|
||||
linitial(op->args) = fix_indexqual_operand(linitial(op->args),
|
||||
index,
|
||||
indexcol);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "unsupported indexorderby type: %d",
|
||||
(int) nodeTag(clause));
|
||||
|
||||
clause = fix_indexqual_clause(root, index, indexcol, clause, NIL);
|
||||
fixed_indexorderbys = lappend(fixed_indexorderbys, clause);
|
||||
}
|
||||
|
||||
return fixed_indexorderbys;
|
||||
}
|
||||
|
||||
/*
|
||||
* fix_indexqual_clause
|
||||
* Convert a single indexqual clause to the form needed by the executor.
|
||||
*
|
||||
* We replace nestloop params here, and replace the index key variables
|
||||
* or expressions by index Var nodes.
|
||||
*/
|
||||
static Node *
|
||||
fix_indexqual_clause(PlannerInfo *root, IndexOptInfo *index, int indexcol,
|
||||
Node *clause, List *indexcolnos)
|
||||
{
|
||||
/*
|
||||
* Replace any outer-relation variables with nestloop params.
|
||||
*
|
||||
* This also makes a copy of the clause, so it's safe to modify it
|
||||
* in-place below.
|
||||
*/
|
||||
clause = replace_nestloop_params(root, clause);
|
||||
|
||||
if (IsA(clause, OpExpr))
|
||||
{
|
||||
OpExpr *op = (OpExpr *) clause;
|
||||
|
||||
/* Replace the indexkey expression with an index Var. */
|
||||
linitial(op->args) = fix_indexqual_operand(linitial(op->args),
|
||||
index,
|
||||
indexcol);
|
||||
}
|
||||
else if (IsA(clause, RowCompareExpr))
|
||||
{
|
||||
RowCompareExpr *rc = (RowCompareExpr *) clause;
|
||||
ListCell *lca,
|
||||
*lcai;
|
||||
|
||||
/* Replace the indexkey expressions with index Vars. */
|
||||
Assert(list_length(rc->largs) == list_length(indexcolnos));
|
||||
forboth(lca, rc->largs, lcai, indexcolnos)
|
||||
{
|
||||
lfirst(lca) = fix_indexqual_operand(lfirst(lca),
|
||||
index,
|
||||
lfirst_int(lcai));
|
||||
}
|
||||
}
|
||||
else if (IsA(clause, ScalarArrayOpExpr))
|
||||
{
|
||||
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
|
||||
|
||||
/* Replace the indexkey expression with an index Var. */
|
||||
linitial(saop->args) = fix_indexqual_operand(linitial(saop->args),
|
||||
index,
|
||||
indexcol);
|
||||
}
|
||||
else if (IsA(clause, NullTest))
|
||||
{
|
||||
NullTest *nt = (NullTest *) clause;
|
||||
|
||||
/* Replace the indexkey expression with an index Var. */
|
||||
nt->arg = (Expr *) fix_indexqual_operand((Node *) nt->arg,
|
||||
index,
|
||||
indexcol);
|
||||
}
|
||||
else
|
||||
elog(ERROR, "unsupported indexqual type: %d",
|
||||
(int) nodeTag(clause));
|
||||
|
||||
return clause;
|
||||
}
|
||||
|
||||
/*
|
||||
* fix_indexqual_operand
|
||||
* Convert an indexqual expression to a Var referencing the index column.
|
||||
|
@ -6136,7 +6136,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
|
||||
|
||||
/* Estimate the cost of index scan */
|
||||
indexScanPath = create_index_path(root, indexInfo,
|
||||
NIL, NIL, NIL, NIL, NIL,
|
||||
NIL, NIL, NIL, NIL,
|
||||
ForwardScanDirection, false,
|
||||
NULL, 1.0, false);
|
||||
|
||||
|
@ -2156,71 +2156,6 @@ CommuteOpExpr(OpExpr *clause)
|
||||
lsecond(clause->args) = temp;
|
||||
}
|
||||
|
||||
/*
|
||||
* CommuteRowCompareExpr: commute a RowCompareExpr clause
|
||||
*
|
||||
* XXX the clause is destructively modified!
|
||||
*/
|
||||
void
|
||||
CommuteRowCompareExpr(RowCompareExpr *clause)
|
||||
{
|
||||
List *newops;
|
||||
List *temp;
|
||||
ListCell *l;
|
||||
|
||||
/* Sanity checks: caller is at fault if these fail */
|
||||
if (!IsA(clause, RowCompareExpr))
|
||||
elog(ERROR, "expected a RowCompareExpr");
|
||||
|
||||
/* Build list of commuted operators */
|
||||
newops = NIL;
|
||||
foreach(l, clause->opnos)
|
||||
{
|
||||
Oid opoid = lfirst_oid(l);
|
||||
|
||||
opoid = get_commutator(opoid);
|
||||
if (!OidIsValid(opoid))
|
||||
elog(ERROR, "could not find commutator for operator %u",
|
||||
lfirst_oid(l));
|
||||
newops = lappend_oid(newops, opoid);
|
||||
}
|
||||
|
||||
/*
|
||||
* modify the clause in-place!
|
||||
*/
|
||||
switch (clause->rctype)
|
||||
{
|
||||
case ROWCOMPARE_LT:
|
||||
clause->rctype = ROWCOMPARE_GT;
|
||||
break;
|
||||
case ROWCOMPARE_LE:
|
||||
clause->rctype = ROWCOMPARE_GE;
|
||||
break;
|
||||
case ROWCOMPARE_GE:
|
||||
clause->rctype = ROWCOMPARE_LE;
|
||||
break;
|
||||
case ROWCOMPARE_GT:
|
||||
clause->rctype = ROWCOMPARE_LT;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unexpected RowCompare type: %d",
|
||||
(int) clause->rctype);
|
||||
break;
|
||||
}
|
||||
|
||||
clause->opnos = newops;
|
||||
|
||||
/*
|
||||
* Note: we need not change the opfamilies list; we assume any btree
|
||||
* opfamily containing an operator will also contain its commutator.
|
||||
* Collations don't change either.
|
||||
*/
|
||||
|
||||
temp = clause->largs;
|
||||
clause->largs = clause->rargs;
|
||||
clause->rargs = temp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper for eval_const_expressions: check that datatype of an attribute
|
||||
* is still what it was when the expression was parsed. This is needed to
|
||||
|
@ -1001,10 +1001,8 @@ create_samplescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer
|
||||
* Creates a path node for an index scan.
|
||||
*
|
||||
* 'index' is a usable index.
|
||||
* 'indexclauses' is a list of RestrictInfo nodes representing clauses
|
||||
* to be used as index qual conditions in the scan.
|
||||
* 'indexclausecols' is an integer list of index column numbers (zero based)
|
||||
* the indexclauses can be used with.
|
||||
* 'indexclauses' is a list of IndexClause nodes representing clauses
|
||||
* to be enforced as qual conditions in the scan.
|
||||
* 'indexorderbys' is a list of bare expressions (no RestrictInfos)
|
||||
* to be used as index ordering operators in the scan.
|
||||
* 'indexorderbycols' is an integer list of index column numbers (zero based)
|
||||
@ -1025,7 +1023,6 @@ IndexPath *
|
||||
create_index_path(PlannerInfo *root,
|
||||
IndexOptInfo *index,
|
||||
List *indexclauses,
|
||||
List *indexclausecols,
|
||||
List *indexorderbys,
|
||||
List *indexorderbycols,
|
||||
List *pathkeys,
|
||||
@ -1037,8 +1034,6 @@ create_index_path(PlannerInfo *root,
|
||||
{
|
||||
IndexPath *pathnode = makeNode(IndexPath);
|
||||
RelOptInfo *rel = index->rel;
|
||||
List *indexquals,
|
||||
*indexqualcols;
|
||||
|
||||
pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan;
|
||||
pathnode->path.parent = rel;
|
||||
@ -1050,15 +1045,8 @@ create_index_path(PlannerInfo *root,
|
||||
pathnode->path.parallel_workers = 0;
|
||||
pathnode->path.pathkeys = pathkeys;
|
||||
|
||||
/* Convert clauses to indexquals the executor can handle */
|
||||
expand_indexqual_conditions(index, indexclauses, indexclausecols,
|
||||
&indexquals, &indexqualcols);
|
||||
|
||||
/* Fill in the pathnode */
|
||||
pathnode->indexinfo = index;
|
||||
pathnode->indexclauses = indexclauses;
|
||||
pathnode->indexquals = indexquals;
|
||||
pathnode->indexqualcols = indexqualcols;
|
||||
pathnode->indexorderbys = indexorderbys;
|
||||
pathnode->indexorderbycols = indexorderbycols;
|
||||
pathnode->indexscandir = indexscandir;
|
||||
@ -3809,7 +3797,6 @@ do { \
|
||||
|
||||
FLAT_COPY_PATH(ipath, path, IndexPath);
|
||||
ADJUST_CHILD_ATTRS(ipath->indexclauses);
|
||||
ADJUST_CHILD_ATTRS(ipath->indexquals);
|
||||
new_path = (Path *) ipath;
|
||||
}
|
||||
break;
|
||||
|
@ -288,6 +288,70 @@ make_sub_restrictinfos(Expr *clause,
|
||||
nullable_relids);
|
||||
}
|
||||
|
||||
/*
|
||||
* commute_restrictinfo
|
||||
*
|
||||
* Given a RestrictInfo containing a binary opclause, produce a RestrictInfo
|
||||
* representing the commutation of that clause. The caller must pass the
|
||||
* OID of the commutator operator (which it's presumably looked up, else
|
||||
* it would not know this is valid).
|
||||
*
|
||||
* Beware that the result shares sub-structure with the given RestrictInfo.
|
||||
* That's okay for the intended usage with derived index quals, but might
|
||||
* be hazardous if the source is subject to change. Also notice that we
|
||||
* assume without checking that the commutator op is a member of the same
|
||||
* btree and hash opclasses as the original op.
|
||||
*/
|
||||
RestrictInfo *
|
||||
commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op)
|
||||
{
|
||||
RestrictInfo *result;
|
||||
OpExpr *newclause;
|
||||
OpExpr *clause = castNode(OpExpr, rinfo->clause);
|
||||
|
||||
Assert(list_length(clause->args) == 2);
|
||||
|
||||
/* flat-copy all the fields of clause ... */
|
||||
newclause = makeNode(OpExpr);
|
||||
memcpy(newclause, clause, sizeof(OpExpr));
|
||||
|
||||
/* ... and adjust those we need to change to commute it */
|
||||
newclause->opno = comm_op;
|
||||
newclause->opfuncid = InvalidOid;
|
||||
newclause->args = list_make2(lsecond(clause->args),
|
||||
linitial(clause->args));
|
||||
|
||||
/* likewise, flat-copy all the fields of rinfo ... */
|
||||
result = makeNode(RestrictInfo);
|
||||
memcpy(result, rinfo, sizeof(RestrictInfo));
|
||||
|
||||
/*
|
||||
* ... and adjust those we need to change. Note in particular that we can
|
||||
* preserve any cached selectivity or cost estimates, since those ought to
|
||||
* be the same for the new clause. Likewise we can keep the source's
|
||||
* parent_ec.
|
||||
*/
|
||||
result->clause = (Expr *) newclause;
|
||||
result->left_relids = rinfo->right_relids;
|
||||
result->right_relids = rinfo->left_relids;
|
||||
Assert(result->orclause == NULL);
|
||||
result->left_ec = rinfo->right_ec;
|
||||
result->right_ec = rinfo->left_ec;
|
||||
result->left_em = rinfo->right_em;
|
||||
result->right_em = rinfo->left_em;
|
||||
result->scansel_cache = NIL; /* not worth updating this */
|
||||
if (rinfo->hashjoinoperator == clause->opno)
|
||||
result->hashjoinoperator = comm_op;
|
||||
else
|
||||
result->hashjoinoperator = InvalidOid;
|
||||
result->left_bucketsize = rinfo->right_bucketsize;
|
||||
result->right_bucketsize = rinfo->left_bucketsize;
|
||||
result->left_mcvfreq = rinfo->right_mcvfreq;
|
||||
result->right_mcvfreq = rinfo->left_mcvfreq;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* restriction_is_or_clause
|
||||
*
|
||||
|
Reference in New Issue
Block a user