aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2019-02-09 17:30:43 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2019-02-09 17:30:43 -0500
commit1a8d5afb0dfc5d0dcc6eda0656a34cb1f0cf0bdf (patch)
tree05bf4d168989789a2b4bbf5c62590c75a0267df7 /src/backend/optimizer/util
parent6401583863eaf83624994908911350b03f9978ae (diff)
downloadpostgresql-1a8d5afb0dfc5d0dcc6eda0656a34cb1f0cf0bdf.tar.gz
postgresql-1a8d5afb0dfc5d0dcc6eda0656a34cb1f0cf0bdf.zip
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
Diffstat (limited to 'src/backend/optimizer/util')
-rw-r--r--src/backend/optimizer/util/clauses.c65
-rw-r--r--src/backend/optimizer/util/pathnode.c17
-rw-r--r--src/backend/optimizer/util/restrictinfo.c64
3 files changed, 66 insertions, 80 deletions
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 663fa7cd339..d7ff17c363d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2157,71 +2157,6 @@ CommuteOpExpr(OpExpr *clause)
}
/*
- * 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
* guard against improper simplification after ALTER COLUMN TYPE. (XXX we
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 08133a28fd2..a3e64110d36 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -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;
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 1c47c708028..03e5f12d0da 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -289,6 +289,70 @@ make_sub_restrictinfos(Expr *clause,
}
/*
+ * 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
*
* Returns t iff the restrictinfo node contains an 'or' clause.