Commit 1a8d5afb authored by Tom Lane's avatar Tom Lane

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
parent 64015838
...@@ -2192,6 +2192,17 @@ expression_tree_walker(Node *node, ...@@ -2192,6 +2192,17 @@ expression_tree_walker(Node *node,
/* groupClauses are deemed uninteresting */ /* groupClauses are deemed uninteresting */
} }
break; break;
case T_IndexClause:
{
IndexClause *iclause = (IndexClause *) node;
if (walker(iclause->rinfo, context))
return true;
if (expression_tree_walker((Node *) iclause->indexquals,
walker, context))
return true;
}
break;
case T_PlaceHolderVar: case T_PlaceHolderVar:
return walker(((PlaceHolderVar *) node)->phexpr, context); return walker(((PlaceHolderVar *) node)->phexpr, context);
case T_InferenceElem: case T_InferenceElem:
...@@ -2999,6 +3010,17 @@ expression_tree_mutator(Node *node, ...@@ -2999,6 +3010,17 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
case T_IndexClause:
{
IndexClause *iclause = (IndexClause *) node;
IndexClause *newnode;
FLATCOPY(newnode, iclause, IndexClause);
MUTATE(newnode->rinfo, iclause->rinfo, RestrictInfo *);
MUTATE(newnode->indexquals, iclause->indexquals, List *);
return (Node *) newnode;
}
break;
case T_PlaceHolderVar: case T_PlaceHolderVar:
{ {
PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderVar *phv = (PlaceHolderVar *) node;
......
...@@ -1744,8 +1744,6 @@ _outIndexPath(StringInfo str, const IndexPath *node) ...@@ -1744,8 +1744,6 @@ _outIndexPath(StringInfo str, const IndexPath *node)
WRITE_NODE_FIELD(indexinfo); WRITE_NODE_FIELD(indexinfo);
WRITE_NODE_FIELD(indexclauses); WRITE_NODE_FIELD(indexclauses);
WRITE_NODE_FIELD(indexquals);
WRITE_NODE_FIELD(indexqualcols);
WRITE_NODE_FIELD(indexorderbys); WRITE_NODE_FIELD(indexorderbys);
WRITE_NODE_FIELD(indexorderbycols); WRITE_NODE_FIELD(indexorderbycols);
WRITE_ENUM_FIELD(indexscandir, ScanDirection); WRITE_ENUM_FIELD(indexscandir, ScanDirection);
...@@ -2447,6 +2445,18 @@ _outRestrictInfo(StringInfo str, const RestrictInfo *node) ...@@ -2447,6 +2445,18 @@ _outRestrictInfo(StringInfo str, const RestrictInfo *node)
WRITE_OID_FIELD(hashjoinoperator); WRITE_OID_FIELD(hashjoinoperator);
} }
static void
_outIndexClause(StringInfo str, const IndexClause *node)
{
WRITE_NODE_TYPE("INDEXCLAUSE");
WRITE_NODE_FIELD(rinfo);
WRITE_NODE_FIELD(indexquals);
WRITE_BOOL_FIELD(lossy);
WRITE_INT_FIELD(indexcol);
WRITE_NODE_FIELD(indexcols);
}
static void static void
_outPlaceHolderVar(StringInfo str, const PlaceHolderVar *node) _outPlaceHolderVar(StringInfo str, const PlaceHolderVar *node)
{ {
...@@ -4044,6 +4054,9 @@ outNode(StringInfo str, const void *obj) ...@@ -4044,6 +4054,9 @@ outNode(StringInfo str, const void *obj)
case T_RestrictInfo: case T_RestrictInfo:
_outRestrictInfo(str, obj); _outRestrictInfo(str, obj);
break; break;
case T_IndexClause:
_outIndexClause(str, obj);
break;
case T_PlaceHolderVar: case T_PlaceHolderVar:
_outPlaceHolderVar(str, obj); _outPlaceHolderVar(str, obj);
break; break;
......
...@@ -145,7 +145,7 @@ typedef struct ...@@ -145,7 +145,7 @@ typedef struct
QualCost total; QualCost total;
} cost_qual_eval_context; } 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, static MergeScanSelCache *cached_scansel(PlannerInfo *root,
RestrictInfo *rinfo, RestrictInfo *rinfo,
PathKey *pathkey); PathKey *pathkey);
...@@ -517,18 +517,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, ...@@ -517,18 +517,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
{ {
path->path.rows = path->path.param_info->ppi_rows; path->path.rows = path->path.param_info->ppi_rows;
/* qpquals come from the rel's restriction clauses and ppi_clauses */ /* qpquals come from the rel's restriction clauses and ppi_clauses */
qpquals = list_concat( qpquals = list_concat(extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
extract_nonindex_conditions(path->indexinfo->indrestrictinfo, path->indexclauses),
path->indexquals),
extract_nonindex_conditions(path->path.param_info->ppi_clauses, extract_nonindex_conditions(path->path.param_info->ppi_clauses,
path->indexquals)); path->indexclauses));
} }
else else
{ {
path->path.rows = baserel->rows; path->path.rows = baserel->rows;
/* qpquals come from just the rel's restriction clauses */ /* qpquals come from just the rel's restriction clauses */
qpquals = extract_nonindex_conditions(path->indexinfo->indrestrictinfo, qpquals = extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
path->indexquals); path->indexclauses);
} }
if (!enable_indexscan) if (!enable_indexscan)
...@@ -753,20 +752,19 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count, ...@@ -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 * 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 * 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 * them). Here we detect only whether a qual clause is directly redundant
* createplan.c, but the full rules are fairly expensive and we don't want to * with some indexclause. If the index path is chosen for use, createplan.c
* go to that much effort for index paths that don't get selected for the * will try a bit harder to get rid of redundant qual conditions; specifically
* final plan. So we approximate it as quals that don't appear directly in * it will see if quals can be proven to be implied by the indexquals. But
* indexquals and also are not redundant children of the same EquivalenceClass * it does not seem worth the cycles to try to factor that in at this stage,
* as some indexqual. This method neglects some infrequently-relevant * since we're only trying to estimate qual eval costs. Otherwise this must
* considerations, specifically clauses that needn't be checked because they * match the logic in create_indexscan_plan().
* 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 * qual_clauses, and the result, are lists of RestrictInfos.
* remove such unnecessary clauses from the qpquals list if this path is * indexclauses is a list of IndexClauses.
* selected for use.
*/ */
static List * static List *
extract_nonindex_conditions(List *qual_clauses, List *indexquals) extract_nonindex_conditions(List *qual_clauses, List *indexclauses)
{ {
List *result = NIL; List *result = NIL;
ListCell *lc; ListCell *lc;
...@@ -777,10 +775,8 @@ extract_nonindex_conditions(List *qual_clauses, List *indexquals) ...@@ -777,10 +775,8 @@ extract_nonindex_conditions(List *qual_clauses, List *indexquals)
if (rinfo->pseudoconstant) if (rinfo->pseudoconstant)
continue; /* we may drop pseudoconstants here */ continue; /* we may drop pseudoconstants here */
if (list_member_ptr(indexquals, rinfo)) if (is_redundant_with_indexclauses(rinfo, indexclauses))
continue; /* simple duplicate */ continue; /* dup or derived from same EquivalenceClass */
if (is_redundant_derived_clause(rinfo, indexquals))
continue; /* derived from same EquivalenceClass */
/* ... skip the predicate proof attempt createplan.c will try ... */ /* ... skip the predicate proof attempt createplan.c will try ... */
result = lappend(result, rinfo); result = lappend(result, rinfo);
} }
...@@ -4242,8 +4238,7 @@ has_indexed_join_quals(NestPath *joinpath) ...@@ -4242,8 +4238,7 @@ has_indexed_join_quals(NestPath *joinpath)
innerpath->parent->relids, innerpath->parent->relids,
joinrelids)) joinrelids))
{ {
if (!(list_member_ptr(indexclauses, rinfo) || if (!is_redundant_with_indexclauses(rinfo, indexclauses))
is_redundant_derived_clause(rinfo, indexclauses)))
return false; return false;
found_one = true; found_one = true;
} }
......
...@@ -2511,3 +2511,40 @@ is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist) ...@@ -2511,3 +2511,40 @@ is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist)
return false; 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;
}
This diff is collapsed.
This diff is collapsed.
...@@ -6136,7 +6136,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid) ...@@ -6136,7 +6136,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
/* Estimate the cost of index scan */ /* Estimate the cost of index scan */
indexScanPath = create_index_path(root, indexInfo, indexScanPath = create_index_path(root, indexInfo,
NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
ForwardScanDirection, false, ForwardScanDirection, false,
NULL, 1.0, false); NULL, 1.0, false);
......
...@@ -2156,71 +2156,6 @@ CommuteOpExpr(OpExpr *clause) ...@@ -2156,71 +2156,6 @@ CommuteOpExpr(OpExpr *clause)
lsecond(clause->args) = temp; 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 * 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 * 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 ...@@ -1001,10 +1001,8 @@ create_samplescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer
* Creates a path node for an index scan. * Creates a path node for an index scan.
* *
* 'index' is a usable index. * 'index' is a usable index.
* 'indexclauses' is a list of RestrictInfo nodes representing clauses * 'indexclauses' is a list of IndexClause nodes representing clauses
* to be used as index qual conditions in the scan. * to be enforced as qual conditions in the scan.
* 'indexclausecols' is an integer list of index column numbers (zero based)
* the indexclauses can be used with.
* 'indexorderbys' is a list of bare expressions (no RestrictInfos) * 'indexorderbys' is a list of bare expressions (no RestrictInfos)
* to be used as index ordering operators in the scan. * to be used as index ordering operators in the scan.
* 'indexorderbycols' is an integer list of index column numbers (zero based) * 'indexorderbycols' is an integer list of index column numbers (zero based)
...@@ -1025,7 +1023,6 @@ IndexPath * ...@@ -1025,7 +1023,6 @@ IndexPath *
create_index_path(PlannerInfo *root, create_index_path(PlannerInfo *root,
IndexOptInfo *index, IndexOptInfo *index,
List *indexclauses, List *indexclauses,
List *indexclausecols,
List *indexorderbys, List *indexorderbys,
List *indexorderbycols, List *indexorderbycols,
List *pathkeys, List *pathkeys,
...@@ -1037,8 +1034,6 @@ create_index_path(PlannerInfo *root, ...@@ -1037,8 +1034,6 @@ create_index_path(PlannerInfo *root,
{ {
IndexPath *pathnode = makeNode(IndexPath); IndexPath *pathnode = makeNode(IndexPath);
RelOptInfo *rel = index->rel; RelOptInfo *rel = index->rel;
List *indexquals,
*indexqualcols;
pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan; pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan;
pathnode->path.parent = rel; pathnode->path.parent = rel;
...@@ -1050,15 +1045,8 @@ create_index_path(PlannerInfo *root, ...@@ -1050,15 +1045,8 @@ create_index_path(PlannerInfo *root,
pathnode->path.parallel_workers = 0; pathnode->path.parallel_workers = 0;
pathnode->path.pathkeys = pathkeys; 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->indexinfo = index;
pathnode->indexclauses = indexclauses; pathnode->indexclauses = indexclauses;
pathnode->indexquals = indexquals;
pathnode->indexqualcols = indexqualcols;
pathnode->indexorderbys = indexorderbys; pathnode->indexorderbys = indexorderbys;
pathnode->indexorderbycols = indexorderbycols; pathnode->indexorderbycols = indexorderbycols;
pathnode->indexscandir = indexscandir; pathnode->indexscandir = indexscandir;
...@@ -3809,7 +3797,6 @@ do { \ ...@@ -3809,7 +3797,6 @@ do { \
FLAT_COPY_PATH(ipath, path, IndexPath); FLAT_COPY_PATH(ipath, path, IndexPath);
ADJUST_CHILD_ATTRS(ipath->indexclauses); ADJUST_CHILD_ATTRS(ipath->indexclauses);
ADJUST_CHILD_ATTRS(ipath->indexquals);
new_path = (Path *) ipath; new_path = (Path *) ipath;
} }
break; break;
......
...@@ -288,6 +288,70 @@ make_sub_restrictinfos(Expr *clause, ...@@ -288,6 +288,70 @@ make_sub_restrictinfos(Expr *clause,
nullable_relids); 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 * restriction_is_or_clause
* *
......
...@@ -226,6 +226,8 @@ static Selectivity regex_selectivity(const char *patt, int pattlen, ...@@ -226,6 +226,8 @@ static Selectivity regex_selectivity(const char *patt, int pattlen,
static Datum string_to_datum(const char *str, Oid datatype); static Datum string_to_datum(const char *str, Oid datatype);
static Const *string_to_const(const char *str, Oid datatype); static Const *string_to_const(const char *str, Oid datatype);
static Const *string_to_bytea_const(const char *str, size_t str_len); static Const *string_to_bytea_const(const char *str, size_t str_len);
static IndexQualInfo *deconstruct_indexqual(RestrictInfo *rinfo,
IndexOptInfo *index, int indexcol);
static List *add_predicate_to_quals(IndexOptInfo *index, List *indexQuals); static List *add_predicate_to_quals(IndexOptInfo *index, List *indexQuals);
...@@ -6574,21 +6576,72 @@ string_to_bytea_const(const char *str, size_t str_len) ...@@ -6574,21 +6576,72 @@ string_to_bytea_const(const char *str, size_t str_len)
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
/* Extract the actual indexquals (as RestrictInfos) from an IndexClause list */
static List *
get_index_quals(List *indexclauses)
{
List *result = NIL;
ListCell *lc;
foreach(lc, indexclauses)
{
IndexClause *iclause = lfirst_node(IndexClause, lc);
if (iclause->indexquals == NIL)
{
/* rinfo->clause is directly usable as an indexqual */
result = lappend(result, iclause->rinfo);
}
else
{
/* report the derived indexquals */
result = list_concat(result, list_copy(iclause->indexquals));
}
}
return result;
}
List * List *
deconstruct_indexquals(IndexPath *path) deconstruct_indexquals(IndexPath *path)
{ {
List *result = NIL; List *result = NIL;
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
ListCell *lcc, ListCell *lc;
*lci;
forboth(lcc, path->indexquals, lci, path->indexqualcols) foreach(lc, path->indexclauses)
{
IndexClause *iclause = lfirst_node(IndexClause, lc);
int indexcol = iclause->indexcol;
IndexQualInfo *qinfo;
if (iclause->indexquals == NIL)
{
/* rinfo->clause is directly usable as an indexqual */
qinfo = deconstruct_indexqual(iclause->rinfo, index, indexcol);
result = lappend(result, qinfo);
}
else
{
/* Process the derived indexquals */
ListCell *lc2;
foreach(lc2, iclause->indexquals)
{
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lc2);
qinfo = deconstruct_indexqual(rinfo, index, indexcol);
result = lappend(result, qinfo);
}
}
}
return result;
}
static IndexQualInfo *
deconstruct_indexqual(RestrictInfo *rinfo, IndexOptInfo *index, int indexcol)
{
{ {
RestrictInfo *rinfo = lfirst_node(RestrictInfo, lcc);
int indexcol = lfirst_int(lci);
Expr *clause; Expr *clause;
Node *leftop,
*rightop;
IndexQualInfo *qinfo; IndexQualInfo *qinfo;
clause = rinfo->clause; clause = rinfo->clause;
...@@ -6600,57 +6653,25 @@ deconstruct_indexquals(IndexPath *path) ...@@ -6600,57 +6653,25 @@ deconstruct_indexquals(IndexPath *path)
if (IsA(clause, OpExpr)) if (IsA(clause, OpExpr))
{ {
qinfo->clause_op = ((OpExpr *) clause)->opno; qinfo->clause_op = ((OpExpr *) clause)->opno;
leftop = get_leftop(clause); qinfo->other_operand = get_rightop(clause);
rightop = get_rightop(clause);
if (match_index_to_operand(leftop, indexcol, index))
{
qinfo->varonleft = true;
qinfo->other_operand = rightop;
}
else
{
Assert(match_index_to_operand(rightop, indexcol, index));
qinfo->varonleft = false;
qinfo->other_operand = leftop;
}
} }
else if (IsA(clause, RowCompareExpr)) else if (IsA(clause, RowCompareExpr))
{ {
RowCompareExpr *rc = (RowCompareExpr *) clause; RowCompareExpr *rc = (RowCompareExpr *) clause;
qinfo->clause_op = linitial_oid(rc->opnos); qinfo->clause_op = linitial_oid(rc->opnos);
/* Examine only first columns to determine left/right sides */ qinfo->other_operand = (Node *) rc->rargs;
if (match_index_to_operand((Node *) linitial(rc->largs),
indexcol, index))
{
qinfo->varonleft = true;
qinfo->other_operand = (Node *) rc->rargs;
}
else
{
Assert(match_index_to_operand((Node *) linitial(rc->rargs),
indexcol, index));
qinfo->varonleft = false;
qinfo->other_operand = (Node *) rc->largs;
}
} }
else if (IsA(clause, ScalarArrayOpExpr)) else if (IsA(clause, ScalarArrayOpExpr))
{ {
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause; ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
qinfo->clause_op = saop->opno; qinfo->clause_op = saop->opno;
/* index column is always on the left in this case */
Assert(match_index_to_operand((Node *) linitial(saop->args),
indexcol, index));
qinfo->varonleft = true;
qinfo->other_operand = (Node *) lsecond(saop->args); qinfo->other_operand = (Node *) lsecond(saop->args);
} }
else if (IsA(clause, NullTest)) else if (IsA(clause, NullTest))
{ {
qinfo->clause_op = InvalidOid; qinfo->clause_op = InvalidOid;
Assert(match_index_to_operand((Node *) ((NullTest *) clause)->arg,
indexcol, index));
qinfo->varonleft = true;
qinfo->other_operand = NULL; qinfo->other_operand = NULL;
} }
else else
...@@ -6659,9 +6680,8 @@ deconstruct_indexquals(IndexPath *path) ...@@ -6659,9 +6680,8 @@ deconstruct_indexquals(IndexPath *path)
(int) nodeTag(clause)); (int) nodeTag(clause));
} }
result = lappend(result, qinfo); return qinfo;
} }
return result;
} }
/* /*
...@@ -6731,7 +6751,7 @@ genericcostestimate(PlannerInfo *root, ...@@ -6731,7 +6751,7 @@ genericcostestimate(PlannerInfo *root,
GenericCosts *costs) GenericCosts *costs)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *indexQuals = path->indexquals; List *indexQuals = get_index_quals(path->indexclauses);
List *indexOrderBys = path->indexorderbys; List *indexOrderBys = path->indexorderbys;
Cost indexStartupCost; Cost indexStartupCost;
Cost indexTotalCost; Cost indexTotalCost;
...@@ -7052,14 +7072,8 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -7052,14 +7072,8 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
} }
} }
/*
* We would need to commute the clause_op if not varonleft, except
* that we only care if it's equality or not, so that refinement is
* unnecessary.
*/
clause_op = qinfo->clause_op;
/* check for equality operator */ /* check for equality operator */
clause_op = qinfo->clause_op;
if (OidIsValid(clause_op)) if (OidIsValid(clause_op))
{ {
op_strategy = get_op_opfamily_strategy(clause_op, op_strategy = get_op_opfamily_strategy(clause_op,
...@@ -7560,12 +7574,6 @@ gincost_opexpr(PlannerInfo *root, ...@@ -7560,12 +7574,6 @@ gincost_opexpr(PlannerInfo *root,
Oid clause_op = qinfo->clause_op; Oid clause_op = qinfo->clause_op;
Node *operand = qinfo->other_operand; Node *operand = qinfo->other_operand;
if (!qinfo->varonleft)
{
/* must commute the operator */
clause_op = get_commutator(clause_op);
}
/* aggressively reduce to a constant, and look through relabeling */ /* aggressively reduce to a constant, and look through relabeling */
operand = estimate_expression_value(root, operand); operand = estimate_expression_value(root, operand);
...@@ -7728,7 +7736,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -7728,7 +7736,7 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double *indexPages) double *indexPages)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *indexQuals = path->indexquals; List *indexQuals = get_index_quals(path->indexclauses);
List *indexOrderBys = path->indexorderbys; List *indexOrderBys = path->indexorderbys;
List *qinfos; List *qinfos;
ListCell *l; ListCell *l;
...@@ -7831,26 +7839,11 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -7831,26 +7839,11 @@ gincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
numEntries = 1; numEntries = 1;
/* /*
* Include predicate in selectivityQuals (should match * If the index is partial, AND the index predicate with the index-bound
* genericcostestimate) * quals to produce a more accurate idea of the number of rows covered by
* the bound conditions.
*/ */
if (index->indpred != NIL) selectivityQuals = add_predicate_to_quals(index, indexQuals);
{
List *predExtraQuals = NIL;
foreach(l, index->indpred)
{
Node *predQual = (Node *) lfirst(l);
List *oneQual = list_make1(predQual);
if (!predicate_implied_by(oneQual, indexQuals, false))
predExtraQuals = list_concat(predExtraQuals, oneQual);
}
/* list_concat avoids modifying the passed-in indexQuals list */
selectivityQuals = list_concat(predExtraQuals, indexQuals);
}
else
selectivityQuals = indexQuals;
/* Estimate the fraction of main-table tuples that will be visited */ /* Estimate the fraction of main-table tuples that will be visited */
*indexSelectivity = clauselist_selectivity(root, selectivityQuals, *indexSelectivity = clauselist_selectivity(root, selectivityQuals,
...@@ -8053,7 +8046,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count, ...@@ -8053,7 +8046,7 @@ brincostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
double *indexPages) double *indexPages)
{ {
IndexOptInfo *index = path->indexinfo; IndexOptInfo *index = path->indexinfo;
List *indexQuals = path->indexquals; List *indexQuals = get_index_quals(path->indexclauses);
double numPages = index->pages; double numPages = index->pages;
RelOptInfo *baserel = index->rel; RelOptInfo *baserel = index->rel;
RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root); RangeTblEntry *rte = planner_rt_fetch(baserel->relid, root);
......
...@@ -262,6 +262,7 @@ typedef enum NodeTag ...@@ -262,6 +262,7 @@ typedef enum NodeTag
T_PathKey, T_PathKey,
T_PathTarget, T_PathTarget,
T_RestrictInfo, T_RestrictInfo,
T_IndexClause,
T_PlaceHolderVar, T_PlaceHolderVar,
T_SpecialJoinInfo, T_SpecialJoinInfo,
T_AppendRelInfo, T_AppendRelInfo,
......
...@@ -1123,30 +1123,16 @@ typedef struct Path ...@@ -1123,30 +1123,16 @@ typedef struct Path
* *
* 'indexinfo' is the index to be scanned. * 'indexinfo' is the index to be scanned.
* *
* 'indexclauses' is a list of index qualification clauses, with implicit * 'indexclauses' is a list of IndexClause nodes, each representing one
* AND semantics across the list. Each clause is a RestrictInfo node from * index-checkable restriction, with implicit AND semantics across the list.
* the query's WHERE or JOIN conditions. An empty list implies a full * An empty list implies a full index scan.
* index scan.
*
* 'indexquals' has the same structure as 'indexclauses', but it contains
* the actual index qual conditions that can be used with the index.
* In simple cases this is identical to 'indexclauses', but when special
* indexable operators appear in 'indexclauses', they are replaced by the
* derived indexscannable conditions in 'indexquals'.
*
* 'indexqualcols' is an integer list of index column numbers (zero-based)
* of the same length as 'indexquals', showing which index column each qual
* is meant to be used with. 'indexquals' is required to be ordered by
* index column, so 'indexqualcols' must form a nondecreasing sequence.
* (The order of multiple quals for the same index column is unspecified.)
* *
* 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have * 'indexorderbys', if not NIL, is a list of ORDER BY expressions that have
* been found to be usable as ordering operators for an amcanorderbyop index. * been found to be usable as ordering operators for an amcanorderbyop index.
* The list must match the path's pathkeys, ie, one expression per pathkey * The list must match the path's pathkeys, ie, one expression per pathkey
* in the same order. These are not RestrictInfos, just bare expressions, * in the same order. These are not RestrictInfos, just bare expressions,
* since they generally won't yield booleans. Also, unlike the case for * since they generally won't yield booleans. It's guaranteed that each
* quals, it's guaranteed that each expression has the index key on the left * expression has the index key on the left side of the operator.
* side of the operator.
* *
* 'indexorderbycols' is an integer list of index column numbers (zero-based) * 'indexorderbycols' is an integer list of index column numbers (zero-based)
* of the same length as 'indexorderbys', showing which index column each * of the same length as 'indexorderbys', showing which index column each
...@@ -1172,8 +1158,6 @@ typedef struct IndexPath ...@@ -1172,8 +1158,6 @@ typedef struct IndexPath
Path path; Path path;
IndexOptInfo *indexinfo; IndexOptInfo *indexinfo;
List *indexclauses; List *indexclauses;
List *indexquals;
List *indexqualcols;
List *indexorderbys; List *indexorderbys;
List *indexorderbycols; List *indexorderbycols;
ScanDirection indexscandir; ScanDirection indexscandir;
...@@ -1181,6 +1165,56 @@ typedef struct IndexPath ...@@ -1181,6 +1165,56 @@ typedef struct IndexPath
Selectivity indexselectivity; Selectivity indexselectivity;
} IndexPath; } IndexPath;
/*
* Each IndexClause references a RestrictInfo node from the query's WHERE
* or JOIN conditions, and shows how that restriction can be applied to
* the particular index. We support both indexclauses that are directly
* usable by the index machinery, which are typically of the form
* "indexcol OP pseudoconstant", and those from which an indexable qual
* can be derived. The simplest such transformation is that a clause
* of the form "pseudoconstant OP indexcol" can be commuted to produce an
* indexable qual (the index machinery expects the indexcol to be on the
* left always). Another example is that we might be able to extract an
* indexable range condition from a LIKE condition, as in "x LIKE 'foo%bar'"
* giving rise to "x >= 'foo' AND x < 'fop'". Derivation of such lossy
* conditions is done by a planner support function attached to the
* indexclause's top-level function or operator.
*
* If indexquals is NIL, it means that rinfo->clause is directly usable as
* an indexqual. Otherwise indexquals contains one or more directly-usable
* indexqual conditions extracted from the given clause. The 'lossy' flag
* indicates whether the indexquals are semantically equivalent to the
* original clause, or form a weaker condition.
*
* Currently, entries in indexquals are RestrictInfos, but they could perhaps
* be bare clauses instead; the only advantage of making them RestrictInfos
* is the possibility of caching cost and selectivity information across
* multiple uses, and it's not clear that that's really worth the price of
* constructing RestrictInfos for them. Note however that the extended-stats
* machinery won't do anything with non-RestrictInfo clauses, so that would
* have to be fixed.
*
* Normally, indexcol is the index of the single index column the clause
* works on, and indexcols is NIL. But if the clause is a RowCompareExpr,
* indexcol is the index of the leading column, and indexcols is a list of
* all the affected columns. (Note that indexcols matches up with the
* columns of the actual indexable RowCompareExpr, which might be in
* indexquals rather than rinfo.)
*
* An IndexPath's IndexClause list is required to be ordered by index
* column, i.e. the indexcol values must form a nondecreasing sequence.
* (The order of multiple clauses for the same index column is unspecified.)
*/
typedef struct IndexClause
{
NodeTag type;
struct RestrictInfo *rinfo; /* original restriction or join clause */
List *indexquals; /* indexqual(s) derived from it, or NIL */
bool lossy; /* are indexquals a lossy version of clause? */
AttrNumber indexcol; /* index column the clause uses (zero-based) */
List *indexcols; /* multiple index columns, if RowCompare */
} IndexClause;
/* /*
* BitmapHeapPath represents one or more indexscans that generate TID bitmaps * BitmapHeapPath represents one or more indexscans that generate TID bitmaps
* instead of directly accessing the heap, followed by AND/OR combinations * instead of directly accessing the heap, followed by AND/OR combinations
......
...@@ -51,7 +51,6 @@ extern bool is_pseudo_constant_clause_relids(Node *clause, Relids relids); ...@@ -51,7 +51,6 @@ extern bool is_pseudo_constant_clause_relids(Node *clause, Relids relids);
extern int NumRelids(Node *clause); extern int NumRelids(Node *clause);
extern void CommuteOpExpr(OpExpr *clause); extern void CommuteOpExpr(OpExpr *clause);
extern void CommuteRowCompareExpr(RowCompareExpr *clause);
extern Query *inline_set_returning_function(PlannerInfo *root, extern Query *inline_set_returning_function(PlannerInfo *root,
RangeTblEntry *rte); RangeTblEntry *rte);
......
...@@ -41,7 +41,6 @@ extern Path *create_samplescan_path(PlannerInfo *root, RelOptInfo *rel, ...@@ -41,7 +41,6 @@ extern Path *create_samplescan_path(PlannerInfo *root, RelOptInfo *rel,
extern IndexPath *create_index_path(PlannerInfo *root, extern IndexPath *create_index_path(PlannerInfo *root,
IndexOptInfo *index, IndexOptInfo *index,
List *indexclauses, List *indexclauses,
List *indexclausecols,
List *indexorderbys, List *indexorderbys,
List *indexorderbycols, List *indexorderbycols,
List *pathkeys, List *pathkeys,
......
...@@ -78,15 +78,7 @@ extern bool indexcol_is_bool_constant_for_query(IndexOptInfo *index, ...@@ -78,15 +78,7 @@ extern bool indexcol_is_bool_constant_for_query(IndexOptInfo *index,
int indexcol); int indexcol);
extern bool match_index_to_operand(Node *operand, int indexcol, extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index); IndexOptInfo *index);
extern void expand_indexqual_conditions(IndexOptInfo *index,
List *indexclauses, List *indexclausecols,
List **indexquals_p, List **indexqualcols_p);
extern void check_index_predicates(PlannerInfo *root, RelOptInfo *rel); extern void check_index_predicates(PlannerInfo *root, RelOptInfo *rel);
extern Expr *adjust_rowcompare_for_index(RowCompareExpr *clause,
IndexOptInfo *index,
int indexcol,
List **indexcolnos,
bool *var_on_left_p);
/* /*
* tidpath.h * tidpath.h
...@@ -175,6 +167,8 @@ extern bool eclass_useful_for_merging(PlannerInfo *root, ...@@ -175,6 +167,8 @@ extern bool eclass_useful_for_merging(PlannerInfo *root,
EquivalenceClass *eclass, EquivalenceClass *eclass,
RelOptInfo *rel); RelOptInfo *rel);
extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist); extern bool is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist);
extern bool is_redundant_with_indexclauses(RestrictInfo *rinfo,
List *indexclauses);
/* /*
* pathkeys.c * pathkeys.c
......
...@@ -29,6 +29,7 @@ extern RestrictInfo *make_restrictinfo(Expr *clause, ...@@ -29,6 +29,7 @@ extern RestrictInfo *make_restrictinfo(Expr *clause,
Relids required_relids, Relids required_relids,
Relids outer_relids, Relids outer_relids,
Relids nullable_relids); Relids nullable_relids);
extern RestrictInfo *commute_restrictinfo(RestrictInfo *rinfo, Oid comm_op);
extern bool restriction_is_or_clause(RestrictInfo *restrictinfo); extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
extern bool restriction_is_securely_promotable(RestrictInfo *restrictinfo, extern bool restriction_is_securely_promotable(RestrictInfo *restrictinfo,
RelOptInfo *rel); RelOptInfo *rel);
......
...@@ -108,7 +108,6 @@ typedef struct ...@@ -108,7 +108,6 @@ typedef struct
{ {
RestrictInfo *rinfo; /* the indexqual itself */ RestrictInfo *rinfo; /* the indexqual itself */
int indexcol; /* zero-based index column number */ int indexcol; /* zero-based index column number */
bool varonleft; /* true if index column is on left of qual */
Oid clause_op; /* qual's operator OID, if relevant */ Oid clause_op; /* qual's operator OID, if relevant */
Node *other_operand; /* non-index operand of qual's operator */ Node *other_operand; /* non-index operand of qual's operator */
} IndexQualInfo; } IndexQualInfo;
......
...@@ -1504,7 +1504,7 @@ SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; ...@@ -1504,7 +1504,7 @@ SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
-> Bitmap Heap Scan on quad_point_tbl -> Bitmap Heap Scan on quad_point_tbl
Recheck Cond: ('(1000,1000),(200,200)'::box @> p) Recheck Cond: ('(1000,1000),(200,200)'::box @> p)
-> Bitmap Index Scan on sp_quad_ind -> Bitmap Index Scan on sp_quad_ind
Index Cond: ('(1000,1000),(200,200)'::box @> p) Index Cond: (p <@ '(1000,1000),(200,200)'::box)
(5 rows) (5 rows)
SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p;
...@@ -1623,7 +1623,7 @@ SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; ...@@ -1623,7 +1623,7 @@ SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
-> Bitmap Heap Scan on kd_point_tbl -> Bitmap Heap Scan on kd_point_tbl
Recheck Cond: ('(1000,1000),(200,200)'::box @> p) Recheck Cond: ('(1000,1000),(200,200)'::box @> p)
-> Bitmap Index Scan on sp_kd_ind -> Bitmap Index Scan on sp_kd_ind
Index Cond: ('(1000,1000),(200,200)'::box @> p) Index Cond: (p <@ '(1000,1000),(200,200)'::box)
(5 rows) (5 rows)
SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p;
...@@ -3181,8 +3181,7 @@ explain (costs off) ...@@ -3181,8 +3181,7 @@ explain (costs off)
Limit Limit
-> Index Scan using boolindex_b_i_key on boolindex -> Index Scan using boolindex_b_i_key on boolindex
Index Cond: (b = true) Index Cond: (b = true)
Filter: b (3 rows)
(4 rows)
explain (costs off) explain (costs off)
select * from boolindex where b = true order by i desc limit 10; select * from boolindex where b = true order by i desc limit 10;
...@@ -3191,8 +3190,7 @@ explain (costs off) ...@@ -3191,8 +3190,7 @@ explain (costs off)
Limit Limit
-> Index Scan Backward using boolindex_b_i_key on boolindex -> Index Scan Backward using boolindex_b_i_key on boolindex
Index Cond: (b = true) Index Cond: (b = true)
Filter: b (3 rows)
(4 rows)
explain (costs off) explain (costs off)
select * from boolindex where not b order by i limit 10; select * from boolindex where not b order by i limit 10;
...@@ -3201,8 +3199,7 @@ explain (costs off) ...@@ -3201,8 +3199,7 @@ explain (costs off)
Limit Limit
-> Index Scan using boolindex_b_i_key on boolindex -> Index Scan using boolindex_b_i_key on boolindex
Index Cond: (b = false) Index Cond: (b = false)
Filter: (NOT b) (3 rows)
(4 rows)
-- --
-- Test for multilevel page deletion -- Test for multilevel page deletion
......
...@@ -3078,9 +3078,9 @@ where q1 = thousand or q2 = thousand; ...@@ -3078,9 +3078,9 @@ where q1 = thousand or q2 = thousand;
Recheck Cond: ((q1.q1 = thousand) OR (q2.q2 = thousand)) Recheck Cond: ((q1.q1 = thousand) OR (q2.q2 = thousand))
-> BitmapOr -> BitmapOr
-> Bitmap Index Scan on tenk1_thous_tenthous -> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (q1.q1 = thousand) Index Cond: (thousand = q1.q1)
-> Bitmap Index Scan on tenk1_thous_tenthous -> Bitmap Index Scan on tenk1_thous_tenthous
Index Cond: (q2.q2 = thousand) Index Cond: (thousand = q2.q2)
-> Hash -> Hash
-> Seq Scan on int4_tbl -> Seq Scan on int4_tbl
(15 rows) (15 rows)
...@@ -3332,7 +3332,7 @@ where t1.unique1 = 1; ...@@ -3332,7 +3332,7 @@ where t1.unique1 = 1;
-> Bitmap Heap Scan on tenk1 t2 -> Bitmap Heap Scan on tenk1 t2
Recheck Cond: (t1.hundred = hundred) Recheck Cond: (t1.hundred = hundred)
-> Bitmap Index Scan on tenk1_hundred -> Bitmap Index Scan on tenk1_hundred
Index Cond: (t1.hundred = hundred) Index Cond: (hundred = t1.hundred)
-> Index Scan using tenk1_unique2 on tenk1 t3 -> Index Scan using tenk1_unique2 on tenk1 t3
Index Cond: (unique2 = t2.thousand) Index Cond: (unique2 = t2.thousand)
(11 rows) (11 rows)
...@@ -3352,7 +3352,7 @@ where t1.unique1 = 1; ...@@ -3352,7 +3352,7 @@ where t1.unique1 = 1;
-> Bitmap Heap Scan on tenk1 t2 -> Bitmap Heap Scan on tenk1 t2
Recheck Cond: (t1.hundred = hundred) Recheck Cond: (t1.hundred = hundred)
-> Bitmap Index Scan on tenk1_hundred -> Bitmap Index Scan on tenk1_hundred
Index Cond: (t1.hundred = hundred) Index Cond: (hundred = t1.hundred)
-> Index Scan using tenk1_unique2 on tenk1 t3 -> Index Scan using tenk1_unique2 on tenk1 t3
Index Cond: (unique2 = t2.thousand) Index Cond: (unique2 = t2.thousand)
(11 rows) (11 rows)
...@@ -3408,7 +3408,7 @@ select b.unique1 from ...@@ -3408,7 +3408,7 @@ select b.unique1 from
-> Nested Loop -> Nested Loop
-> Seq Scan on int4_tbl i1 -> Seq Scan on int4_tbl i1
-> Index Scan using tenk1_thous_tenthous on tenk1 b -> Index Scan using tenk1_thous_tenthous on tenk1 b
Index Cond: ((thousand = i1.f1) AND (i2.f1 = tenthous)) Index Cond: ((thousand = i1.f1) AND (tenthous = i2.f1))
-> Index Scan using tenk1_unique1 on tenk1 a -> Index Scan using tenk1_unique1 on tenk1 a
Index Cond: (unique1 = b.unique2) Index Cond: (unique1 = b.unique2)
-> Index Only Scan using tenk1_thous_tenthous on tenk1 c -> Index Only Scan using tenk1_thous_tenthous on tenk1 c
...@@ -3444,7 +3444,7 @@ order by fault; ...@@ -3444,7 +3444,7 @@ order by fault;
Filter: ((COALESCE(tenk1.unique1, '-1'::integer) + int8_tbl.q1) = 122) Filter: ((COALESCE(tenk1.unique1, '-1'::integer) + int8_tbl.q1) = 122)
-> Seq Scan on int8_tbl -> Seq Scan on int8_tbl
-> Index Scan using tenk1_unique2 on tenk1 -> Index Scan using tenk1_unique2 on tenk1
Index Cond: (int8_tbl.q2 = unique2) Index Cond: (unique2 = int8_tbl.q2)
(5 rows) (5 rows)
select * from select * from
...@@ -3499,7 +3499,7 @@ select q1, unique2, thousand, hundred ...@@ -3499,7 +3499,7 @@ select q1, unique2, thousand, hundred
Filter: ((COALESCE(b.thousand, 123) = a.q1) AND (a.q1 = COALESCE(b.hundred, 123))) Filter: ((COALESCE(b.thousand, 123) = a.q1) AND (a.q1 = COALESCE(b.hundred, 123)))
-> Seq Scan on int8_tbl a -> Seq Scan on int8_tbl a
-> Index Scan using tenk1_unique2 on tenk1 b -> Index Scan using tenk1_unique2 on tenk1 b
Index Cond: (a.q1 = unique2) Index Cond: (unique2 = a.q1)
(5 rows) (5 rows)
select q1, unique2, thousand, hundred select q1, unique2, thousand, hundred
...@@ -4586,7 +4586,7 @@ explain (costs off) ...@@ -4586,7 +4586,7 @@ explain (costs off)
Nested Loop Left Join Nested Loop Left Join
-> Seq Scan on int4_tbl x -> Seq Scan on int4_tbl x
-> Index Scan using tenk1_unique1 on tenk1 -> Index Scan using tenk1_unique1 on tenk1
Index Cond: (x.f1 = unique1) Index Cond: (unique1 = x.f1)
(4 rows) (4 rows)
-- check scoping of lateral versus parent references -- check scoping of lateral versus parent references
......
...@@ -647,7 +647,7 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ...@@ -647,7 +647,7 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
-> Seq Scan on prt1_e_p1 t3 -> Seq Scan on prt1_e_p1 t3
Filter: (c = 0) Filter: (c = 0)
-> Index Scan using iprt2_p1_b on prt2_p1 t2 -> Index Scan using iprt2_p1_b on prt2_p1 t2
Index Cond: (t1.a = b) Index Cond: (b = t1.a)
-> Nested Loop Left Join -> Nested Loop Left Join
-> Hash Right Join -> Hash Right Join
Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2)) Hash Cond: (t1_1.a = ((t3_1.a + t3_1.b) / 2))
...@@ -656,7 +656,7 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ...@@ -656,7 +656,7 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
-> Seq Scan on prt1_e_p2 t3_1 -> Seq Scan on prt1_e_p2 t3_1
Filter: (c = 0) Filter: (c = 0)
-> Index Scan using iprt2_p2_b on prt2_p2 t2_1 -> Index Scan using iprt2_p2_b on prt2_p2 t2_1
Index Cond: (t1_1.a = b) Index Cond: (b = t1_1.a)
-> Nested Loop Left Join -> Nested Loop Left Join
-> Hash Right Join -> Hash Right Join
Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2)) Hash Cond: (t1_2.a = ((t3_2.a + t3_2.b) / 2))
...@@ -665,7 +665,7 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ...@@ -665,7 +665,7 @@ SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2
-> Seq Scan on prt1_e_p3 t3_2 -> Seq Scan on prt1_e_p3 t3_2
Filter: (c = 0) Filter: (c = 0)
-> Index Scan using iprt2_p3_b on prt2_p3 t2_2 -> Index Scan using iprt2_p3_b on prt2_p3 t2_2
Index Cond: (t1_2.a = b) Index Cond: (b = t1_2.a)
(30 rows) (30 rows)
SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b; SELECT t1.a, t1.c, t2.b, t2.c, t3.a + t3.b, t3.c FROM (prt1 t1 LEFT JOIN prt2 t2 ON t1.a = t2.b) RIGHT JOIN prt1_e t3 ON (t1.a = (t3.a + t3.b)/2) WHERE t3.c = 0 ORDER BY t1.a, t2.b, t3.a + t3.b;
...@@ -1878,11 +1878,11 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b); ...@@ -1878,11 +1878,11 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1 LEFT JOIN prt2 t2 ON (t1.a < t2.b);
-> Seq Scan on prt1_p3 t1_2 -> Seq Scan on prt1_p3 t1_2
-> Append -> Append
-> Index Scan using iprt2_p1_b on prt2_p1 t2 -> Index Scan using iprt2_p1_b on prt2_p1 t2
Index Cond: (t1.a < b) Index Cond: (b > t1.a)
-> Index Scan using iprt2_p2_b on prt2_p2 t2_1 -> Index Scan using iprt2_p2_b on prt2_p2 t2_1
Index Cond: (t1.a < b) Index Cond: (b > t1.a)
-> Index Scan using iprt2_p3_b on prt2_p3 t2_2 -> Index Scan using iprt2_p3_b on prt2_p3 t2_2
Index Cond: (t1.a < b) Index Cond: (b > t1.a)
(12 rows) (12 rows)
-- equi-join with join condition on partial keys does not qualify for -- equi-join with join condition on partial keys does not qualify for
......
...@@ -2635,17 +2635,17 @@ select * from tbl1 join tprt on tbl1.col1 > tprt.col1; ...@@ -2635,17 +2635,17 @@ select * from tbl1 join tprt on tbl1.col1 > tprt.col1;
-> Seq Scan on tbl1 (actual rows=2 loops=1) -> Seq Scan on tbl1 (actual rows=2 loops=1)
-> Append (actual rows=3 loops=2) -> Append (actual rows=3 loops=2)
-> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=2) -> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=2)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt2_idx on tprt_2 (actual rows=2 loops=1) -> Index Scan using tprt2_idx on tprt_2 (actual rows=2 loops=1)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt3_idx on tprt_3 (never executed) -> Index Scan using tprt3_idx on tprt_3 (never executed)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt4_idx on tprt_4 (never executed) -> Index Scan using tprt4_idx on tprt_4 (never executed)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt5_idx on tprt_5 (never executed) -> Index Scan using tprt5_idx on tprt_5 (never executed)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt6_idx on tprt_6 (never executed) -> Index Scan using tprt6_idx on tprt_6 (never executed)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
(15 rows) (15 rows)
explain (analyze, costs off, summary off, timing off) explain (analyze, costs off, summary off, timing off)
...@@ -2701,17 +2701,17 @@ select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1; ...@@ -2701,17 +2701,17 @@ select * from tbl1 inner join tprt on tbl1.col1 > tprt.col1;
-> Seq Scan on tbl1 (actual rows=5 loops=1) -> Seq Scan on tbl1 (actual rows=5 loops=1)
-> Append (actual rows=5 loops=5) -> Append (actual rows=5 loops=5)
-> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=5) -> Index Scan using tprt1_idx on tprt_1 (actual rows=2 loops=5)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt2_idx on tprt_2 (actual rows=3 loops=4) -> Index Scan using tprt2_idx on tprt_2 (actual rows=3 loops=4)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt3_idx on tprt_3 (actual rows=1 loops=2) -> Index Scan using tprt3_idx on tprt_3 (actual rows=1 loops=2)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt4_idx on tprt_4 (never executed) -> Index Scan using tprt4_idx on tprt_4 (never executed)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt5_idx on tprt_5 (never executed) -> Index Scan using tprt5_idx on tprt_5 (never executed)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
-> Index Scan using tprt6_idx on tprt_6 (never executed) -> Index Scan using tprt6_idx on tprt_6 (never executed)
Index Cond: (tbl1.col1 > col1) Index Cond: (col1 < tbl1.col1)
(15 rows) (15 rows)
explain (analyze, costs off, summary off, timing off) explain (analyze, costs off, summary off, timing off)
...@@ -2786,17 +2786,17 @@ select * from tbl1 join tprt on tbl1.col1 < tprt.col1; ...@@ -2786,17 +2786,17 @@ select * from tbl1 join tprt on tbl1.col1 < tprt.col1;
-> Seq Scan on tbl1 (actual rows=1 loops=1) -> Seq Scan on tbl1 (actual rows=1 loops=1)
-> Append (actual rows=1 loops=1) -> Append (actual rows=1 loops=1)
-> Index Scan using tprt1_idx on tprt_1 (never executed) -> Index Scan using tprt1_idx on tprt_1 (never executed)
Index Cond: (tbl1.col1 < col1) Index Cond: (col1 > tbl1.col1)
-> Index Scan using tprt2_idx on tprt_2 (never executed) -> Index Scan using tprt2_idx on tprt_2 (never executed)
Index Cond: (tbl1.col1 < col1) Index Cond: (col1 > tbl1.col1)
-> Index Scan using tprt3_idx on tprt_3 (never executed) -> Index Scan using tprt3_idx on tprt_3 (never executed)
Index Cond: (tbl1.col1 < col1) Index Cond: (col1 > tbl1.col1)
-> Index Scan using tprt4_idx on tprt_4 (never executed) -> Index Scan using tprt4_idx on tprt_4 (never executed)
Index Cond: (tbl1.col1 < col1) Index Cond: (col1 > tbl1.col1)
-> Index Scan using tprt5_idx on tprt_5 (never executed) -> Index Scan using tprt5_idx on tprt_5 (never executed)
Index Cond: (tbl1.col1 < col1) Index Cond: (col1 > tbl1.col1)
-> Index Scan using tprt6_idx on tprt_6 (actual rows=1 loops=1) -> Index Scan using tprt6_idx on tprt_6 (actual rows=1 loops=1)
Index Cond: (tbl1.col1 < col1) Index Cond: (col1 > tbl1.col1)
(15 rows) (15 rows)
select tbl1.col1, tprt.col1 from tbl1 select tbl1.col1, tprt.col1 from tbl1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment