Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 1a8d5af

Browse files
committed
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
1 parent 6401583 commit 1a8d5af

22 files changed

+698
-622
lines changed

src/backend/nodes/nodeFuncs.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,17 @@ expression_tree_walker(Node *node,
21922192
/* groupClauses are deemed uninteresting */
21932193
}
21942194
break;
2195+
case T_IndexClause:
2196+
{
2197+
IndexClause *iclause = (IndexClause *) node;
2198+
2199+
if (walker(iclause->rinfo, context))
2200+
return true;
2201+
if (expression_tree_walker((Node *) iclause->indexquals,
2202+
walker, context))
2203+
return true;
2204+
}
2205+
break;
21952206
case T_PlaceHolderVar:
21962207
return walker(((PlaceHolderVar *) node)->phexpr, context);
21972208
case T_InferenceElem:
@@ -2999,6 +3010,17 @@ expression_tree_mutator(Node *node,
29993010
return (Node *) newnode;
30003011
}
30013012
break;
3013+
case T_IndexClause:
3014+
{
3015+
IndexClause *iclause = (IndexClause *) node;
3016+
IndexClause *newnode;
3017+
3018+
FLATCOPY(newnode, iclause, IndexClause);
3019+
MUTATE(newnode->rinfo, iclause->rinfo, RestrictInfo *);
3020+
MUTATE(newnode->indexquals, iclause->indexquals, List *);
3021+
return (Node *) newnode;
3022+
}
3023+
break;
30023024
case T_PlaceHolderVar:
30033025
{
30043026
PlaceHolderVar *phv = (PlaceHolderVar *) node;

src/backend/nodes/outfuncs.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,8 +1744,6 @@ _outIndexPath(StringInfo str, const IndexPath *node)
17441744

17451745
WRITE_NODE_FIELD(indexinfo);
17461746
WRITE_NODE_FIELD(indexclauses);
1747-
WRITE_NODE_FIELD(indexquals);
1748-
WRITE_NODE_FIELD(indexqualcols);
17491747
WRITE_NODE_FIELD(indexorderbys);
17501748
WRITE_NODE_FIELD(indexorderbycols);
17511749
WRITE_ENUM_FIELD(indexscandir, ScanDirection);
@@ -2447,6 +2445,18 @@ _outRestrictInfo(StringInfo str, const RestrictInfo *node)
24472445
WRITE_OID_FIELD(hashjoinoperator);
24482446
}
24492447

2448+
static void
2449+
_outIndexClause(StringInfo str, const IndexClause *node)
2450+
{
2451+
WRITE_NODE_TYPE("INDEXCLAUSE");
2452+
2453+
WRITE_NODE_FIELD(rinfo);
2454+
WRITE_NODE_FIELD(indexquals);
2455+
WRITE_BOOL_FIELD(lossy);
2456+
WRITE_INT_FIELD(indexcol);
2457+
WRITE_NODE_FIELD(indexcols);
2458+
}
2459+
24502460
static void
24512461
_outPlaceHolderVar(StringInfo str, const PlaceHolderVar *node)
24522462
{
@@ -4044,6 +4054,9 @@ outNode(StringInfo str, const void *obj)
40444054
case T_RestrictInfo:
40454055
_outRestrictInfo(str, obj);
40464056
break;
4057+
case T_IndexClause:
4058+
_outIndexClause(str, obj);
4059+
break;
40474060
case T_PlaceHolderVar:
40484061
_outPlaceHolderVar(str, obj);
40494062
break;

src/backend/optimizer/path/costsize.c

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ typedef struct
145145
QualCost total;
146146
} cost_qual_eval_context;
147147

148-
static List *extract_nonindex_conditions(List *qual_clauses, List *indexquals);
148+
static List *extract_nonindex_conditions(List *qual_clauses, List *indexclauses);
149149
static MergeScanSelCache *cached_scansel(PlannerInfo *root,
150150
RestrictInfo *rinfo,
151151
PathKey *pathkey);
@@ -517,18 +517,17 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
517517
{
518518
path->path.rows = path->path.param_info->ppi_rows;
519519
/* qpquals come from the rel's restriction clauses and ppi_clauses */
520-
qpquals = list_concat(
521-
extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
522-
path->indexquals),
520+
qpquals = list_concat(extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
521+
path->indexclauses),
523522
extract_nonindex_conditions(path->path.param_info->ppi_clauses,
524-
path->indexquals));
523+
path->indexclauses));
525524
}
526525
else
527526
{
528527
path->path.rows = baserel->rows;
529528
/* qpquals come from just the rel's restriction clauses */
530529
qpquals = extract_nonindex_conditions(path->indexinfo->indrestrictinfo,
531-
path->indexquals);
530+
path->indexclauses);
532531
}
533532

534533
if (!enable_indexscan)
@@ -753,20 +752,19 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
753752
*
754753
* Given a list of quals to be enforced in an indexscan, extract the ones that
755754
* will have to be applied as qpquals (ie, the index machinery won't handle
756-
* them). The actual rules for this appear in create_indexscan_plan() in
757-
* createplan.c, but the full rules are fairly expensive and we don't want to
758-
* go to that much effort for index paths that don't get selected for the
759-
* final plan. So we approximate it as quals that don't appear directly in
760-
* indexquals and also are not redundant children of the same EquivalenceClass
761-
* as some indexqual. This method neglects some infrequently-relevant
762-
* considerations, specifically clauses that needn't be checked because they
763-
* are implied by an indexqual. It does not seem worth the cycles to try to
764-
* factor that in at this stage, even though createplan.c will take pains to
765-
* remove such unnecessary clauses from the qpquals list if this path is
766-
* selected for use.
755+
* them). Here we detect only whether a qual clause is directly redundant
756+
* with some indexclause. If the index path is chosen for use, createplan.c
757+
* will try a bit harder to get rid of redundant qual conditions; specifically
758+
* it will see if quals can be proven to be implied by the indexquals. But
759+
* it does not seem worth the cycles to try to factor that in at this stage,
760+
* since we're only trying to estimate qual eval costs. Otherwise this must
761+
* match the logic in create_indexscan_plan().
762+
*
763+
* qual_clauses, and the result, are lists of RestrictInfos.
764+
* indexclauses is a list of IndexClauses.
767765
*/
768766
static List *
769-
extract_nonindex_conditions(List *qual_clauses, List *indexquals)
767+
extract_nonindex_conditions(List *qual_clauses, List *indexclauses)
770768
{
771769
List *result = NIL;
772770
ListCell *lc;
@@ -777,10 +775,8 @@ extract_nonindex_conditions(List *qual_clauses, List *indexquals)
777775

778776
if (rinfo->pseudoconstant)
779777
continue; /* we may drop pseudoconstants here */
780-
if (list_member_ptr(indexquals, rinfo))
781-
continue; /* simple duplicate */
782-
if (is_redundant_derived_clause(rinfo, indexquals))
783-
continue; /* derived from same EquivalenceClass */
778+
if (is_redundant_with_indexclauses(rinfo, indexclauses))
779+
continue; /* dup or derived from same EquivalenceClass */
784780
/* ... skip the predicate proof attempt createplan.c will try ... */
785781
result = lappend(result, rinfo);
786782
}
@@ -4242,8 +4238,7 @@ has_indexed_join_quals(NestPath *joinpath)
42424238
innerpath->parent->relids,
42434239
joinrelids))
42444240
{
4245-
if (!(list_member_ptr(indexclauses, rinfo) ||
4246-
is_redundant_derived_clause(rinfo, indexclauses)))
4241+
if (!is_redundant_with_indexclauses(rinfo, indexclauses))
42474242
return false;
42484243
found_one = true;
42494244
}

src/backend/optimizer/path/equivclass.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,3 +2511,40 @@ is_redundant_derived_clause(RestrictInfo *rinfo, List *clauselist)
25112511

25122512
return false;
25132513
}
2514+
2515+
/*
2516+
* is_redundant_with_indexclauses
2517+
* Test whether rinfo is redundant with any clause in the IndexClause
2518+
* list. Here, for convenience, we test both simple identity and
2519+
* whether it is derived from the same EC as any member of the list.
2520+
*/
2521+
bool
2522+
is_redundant_with_indexclauses(RestrictInfo *rinfo, List *indexclauses)
2523+
{
2524+
EquivalenceClass *parent_ec = rinfo->parent_ec;
2525+
ListCell *lc;
2526+
2527+
foreach(lc, indexclauses)
2528+
{
2529+
IndexClause *iclause = lfirst_node(IndexClause, lc);
2530+
RestrictInfo *otherrinfo = iclause->rinfo;
2531+
2532+
/* If indexclause is lossy, it won't enforce the condition exactly */
2533+
if (iclause->lossy)
2534+
continue;
2535+
2536+
/* Match if it's same clause (pointer equality should be enough) */
2537+
if (rinfo == otherrinfo)
2538+
return true;
2539+
/* Match if derived from same EC */
2540+
if (parent_ec && otherrinfo->parent_ec == parent_ec)
2541+
return true;
2542+
2543+
/*
2544+
* No need to look at the derived clauses in iclause->indexquals; they
2545+
* couldn't match if the parent clause didn't.
2546+
*/
2547+
}
2548+
2549+
return false;
2550+
}

0 commit comments

Comments
 (0)