simplify-index-pathkeys.patch
text/x-patch
Filename: simplify-index-pathkeys.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/optimizer/path/indxpath.c | 0 | 0 |
| src/backend/optimizer/path/pathkeys.c | 0 | 0 |
| src/backend/optimizer/util/plancat.c | 0 | 0 |
| src/backend/utils/adt/selfuncs.c | 0 | 0 |
| src/include/nodes/relation.h | 0 | 0 |
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index d8fc12068fb6113c0baf8aa4e5941e0150ce3521..f73e0e6dc6007ce768828480f74621752aaf57d2 100644
*** a/src/backend/optimizer/path/indxpath.c
--- b/src/backend/optimizer/path/indxpath.c
*************** find_usable_indexes(PlannerInfo *root, R
*** 380,386 ****
* how many of them are actually useful for this query. This is not
* relevant unless we are at top level.
*/
! index_is_ordered = OidIsValid(index->fwdsortop[0]);
if (index_is_ordered && possibly_useful_pathkeys &&
istoplevel && outer_rel == NULL)
{
--- 380,386 ----
* how many of them are actually useful for this query. This is not
* relevant unless we are at top level.
*/
! index_is_ordered = (index->sortopfamily != NULL);
if (index_is_ordered && possibly_useful_pathkeys &&
istoplevel && outer_rel == NULL)
{
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 8af0c6dc482372244b15a8a5f5a4a562e34ac91b..6325655eed84759168a51aed3f712582ec9c1f00 100644
*** a/src/backend/optimizer/path/pathkeys.c
--- b/src/backend/optimizer/path/pathkeys.c
*************** static PathKey *make_canonical_pathkey(P
*** 36,47 ****
EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first);
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
- static PathKey *make_pathkey_from_sortinfo(PlannerInfo *root,
- Expr *expr, Oid ordering_op,
- bool nulls_first,
- Index sortref,
- bool create_it,
- bool canonicalize);
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
AttrNumber varattno);
static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
--- 36,41 ----
*************** canonicalize_pathkeys(PlannerInfo *root,
*** 224,232 ****
/*
* make_pathkey_from_sortinfo
! * Given an expression, a sortop, and a nulls-first flag, create
! * a PathKey. If canonicalize = true, the result is a "canonical"
! * PathKey, otherwise not. (But note it might be redundant anyway.)
*
* If the PathKey is being generated from a SortGroupClause, sortref should be
* the SortGroupClause's SortGroupRef; otherwise zero.
--- 218,226 ----
/*
* make_pathkey_from_sortinfo
! * Given an expression and sort-order information, create a PathKey.
! * If canonicalize = true, the result is a "canonical" PathKey,
! * otherwise not. (But note it might be redundant anyway.)
*
* If the PathKey is being generated from a SortGroupClause, sortref should be
* the SortGroupClause's SortGroupRef; otherwise zero.
*************** canonicalize_pathkeys(PlannerInfo *root,
*** 240,285 ****
*/
static PathKey *
make_pathkey_from_sortinfo(PlannerInfo *root,
! Expr *expr, Oid ordering_op,
bool nulls_first,
Index sortref,
bool create_it,
bool canonicalize)
{
- Oid opfamily,
- opcintype;
int16 strategy;
Oid equality_op;
List *opfamilies;
EquivalenceClass *eclass;
/*
! * An ordering operator fully determines the behavior of its opfamily, so
! * could only meaningfully appear in one family --- or perhaps two if one
! * builds a reverse-sort opfamily, but there's not much point in that
! * anymore. But EquivalenceClasses need to contain opfamily lists based
! * on the family membership of equality operators, which could easily be
! * bigger. So, look up the equality operator that goes with the ordering
! * operator (this should be unique) and get its membership.
*/
-
- /* Find the operator in pg_amop --- failure shouldn't happen */
- if (!get_ordering_op_properties(ordering_op,
- &opfamily, &opcintype, &strategy))
- elog(ERROR, "operator %u is not a valid ordering operator",
- ordering_op);
- /* Get matching equality operator */
equality_op = get_opfamily_member(opfamily,
opcintype,
opcintype,
BTEqualStrategyNumber);
if (!OidIsValid(equality_op)) /* shouldn't happen */
! elog(ERROR, "could not find equality operator for ordering operator %u",
! ordering_op);
opfamilies = get_mergejoin_opfamilies(equality_op);
if (!opfamilies) /* certainly should find some */
! elog(ERROR, "could not find opfamilies for ordering operator %u",
! ordering_op);
/*
* When dealing with binary-compatible opclasses, we have to ensure that
--- 234,272 ----
*/
static PathKey *
make_pathkey_from_sortinfo(PlannerInfo *root,
! Expr *expr,
! Oid opfamily,
! Oid opcintype,
! bool reverse_sort,
bool nulls_first,
Index sortref,
bool create_it,
bool canonicalize)
{
int16 strategy;
Oid equality_op;
List *opfamilies;
EquivalenceClass *eclass;
+ strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber;
+
/*
! * EquivalenceClasses need to contain opfamily lists based on the family
! * membership of mergejoinable equality operators, which could belong to
! * more than one opfamily. So we have to look up the opfamily's equality
! * operator and get its membership.
*/
equality_op = get_opfamily_member(opfamily,
opcintype,
opcintype,
BTEqualStrategyNumber);
if (!OidIsValid(equality_op)) /* shouldn't happen */
! elog(ERROR, "could not find equality operator for opfamily %u",
! opfamily);
opfamilies = get_mergejoin_opfamilies(equality_op);
if (!opfamilies) /* certainly should find some */
! elog(ERROR, "could not find opfamilies for equality operator %u",
! equality_op);
/*
* When dealing with binary-compatible opclasses, we have to ensure that
*************** make_pathkey_from_sortinfo(PlannerInfo *
*** 322,327 ****
--- 309,350 ----
return makePathKey(eclass, opfamily, strategy, nulls_first);
}
+ /*
+ * make_pathkey_from_sortop
+ * Like make_pathkey_from_sortinfo, but work from a sort operator.
+ *
+ * This should eventually go away, but we need to restructure SortGroupClause
+ * first.
+ */
+ static PathKey *
+ make_pathkey_from_sortop(PlannerInfo *root,
+ Expr *expr,
+ Oid ordering_op,
+ bool nulls_first,
+ Index sortref,
+ bool create_it,
+ bool canonicalize)
+ {
+ Oid opfamily,
+ opcintype;
+ int16 strategy;
+
+ /* Find the operator in pg_amop --- failure shouldn't happen */
+ if (!get_ordering_op_properties(ordering_op,
+ &opfamily, &opcintype, &strategy))
+ elog(ERROR, "operator %u is not a valid ordering operator",
+ ordering_op);
+ return make_pathkey_from_sortinfo(root,
+ expr,
+ opfamily,
+ opcintype,
+ (strategy == BTGreaterStrategyNumber),
+ nulls_first,
+ sortref,
+ create_it,
+ canonicalize);
+ }
+
/****************************************************************************
* PATHKEY COMPARISONS
*************** get_cheapest_fractional_path_for_pathkey
*** 479,489 ****
* build_index_pathkeys
* Build a pathkeys list that describes the ordering induced by an index
* scan using the given index. (Note that an unordered index doesn't
! * induce any ordering; such an index will have no sortop OIDS in
! * its sortops arrays, and we will return NIL.)
*
! * If 'scandir' is BackwardScanDirection, attempt to build pathkeys
! * representing a backwards scan of the index. Return NIL if can't do it.
*
* The result is canonical, meaning that redundant pathkeys are removed;
* it may therefore have fewer entries than there are index columns.
--- 502,511 ----
* build_index_pathkeys
* Build a pathkeys list that describes the ordering induced by an index
* scan using the given index. (Note that an unordered index doesn't
! * induce any ordering, so we return NIL.)
*
! * If 'scandir' is BackwardScanDirection, build pathkeys representing a
! * backwards scan of the index.
*
* The result is canonical, meaning that redundant pathkeys are removed;
* it may therefore have fewer entries than there are index columns.
*************** build_index_pathkeys(PlannerInfo *root,
*** 500,511 ****
ScanDirection scandir)
{
List *retval = NIL;
! ListCell *indexprs_item = list_head(index->indexprs);
int i;
for (i = 0; i < index->ncolumns; i++)
{
! Oid sortop;
bool nulls_first;
int ikey;
Expr *indexkey;
--- 522,537 ----
ScanDirection scandir)
{
List *retval = NIL;
! ListCell *indexprs_item;
int i;
+ if (index->sortopfamily == NULL)
+ return NIL; /* non-orderable index */
+
+ indexprs_item = list_head(index->indexprs);
for (i = 0; i < index->ncolumns; i++)
{
! bool reverse_sort;
bool nulls_first;
int ikey;
Expr *indexkey;
*************** build_index_pathkeys(PlannerInfo *root,
*** 513,530 ****
if (ScanDirectionIsBackward(scandir))
{
! sortop = index->revsortop[i];
nulls_first = !index->nulls_first[i];
}
else
{
! sortop = index->fwdsortop[i];
nulls_first = index->nulls_first[i];
}
- if (!OidIsValid(sortop))
- break; /* no more orderable columns */
-
ikey = index->indexkeys[i];
if (ikey != 0)
{
--- 539,553 ----
if (ScanDirectionIsBackward(scandir))
{
! reverse_sort = !index->reverse_sort[i];
nulls_first = !index->nulls_first[i];
}
else
{
! reverse_sort = index->reverse_sort[i];
nulls_first = index->nulls_first[i];
}
ikey = index->indexkeys[i];
if (ikey != 0)
{
*************** build_index_pathkeys(PlannerInfo *root,
*** 543,549 ****
/* OK, try to make a canonical pathkey for this sort key */
cpathkey = make_pathkey_from_sortinfo(root,
indexkey,
! sortop,
nulls_first,
0,
false,
--- 566,574 ----
/* OK, try to make a canonical pathkey for this sort key */
cpathkey = make_pathkey_from_sortinfo(root,
indexkey,
! index->sortopfamily[i],
! index->opcintype[i],
! reverse_sort,
nulls_first,
0,
false,
*************** make_pathkeys_for_sortclauses(PlannerInf
*** 892,904 ****
sortkey = (Expr *) get_sortgroupclause_expr(sortcl, tlist);
Assert(OidIsValid(sortcl->sortop));
! pathkey = make_pathkey_from_sortinfo(root,
! sortkey,
! sortcl->sortop,
! sortcl->nulls_first,
! sortcl->tleSortGroupRef,
! true,
! canonicalize);
/* Canonical form eliminates redundant ordering keys */
if (canonicalize)
--- 917,929 ----
sortkey = (Expr *) get_sortgroupclause_expr(sortcl, tlist);
Assert(OidIsValid(sortcl->sortop));
! pathkey = make_pathkey_from_sortop(root,
! sortkey,
! sortcl->sortop,
! sortcl->nulls_first,
! sortcl->tleSortGroupRef,
! true,
! canonicalize);
/* Canonical form eliminates redundant ordering keys */
if (canonicalize)
*************** make_pathkeys_for_aggregate(PlannerInfo
*** 935,947 ****
* We arbitrarily set nulls_first to false. Actually, a MIN/MAX agg can
* use either nulls ordering option, but that is dealt with elsewhere.
*/
! pathkey = make_pathkey_from_sortinfo(root,
! aggtarget,
! aggsortop,
! false, /* nulls_first */
! 0,
! true,
! false);
return list_make1(pathkey);
}
--- 960,972 ----
* We arbitrarily set nulls_first to false. Actually, a MIN/MAX agg can
* use either nulls ordering option, but that is dealt with elsewhere.
*/
! pathkey = make_pathkey_from_sortop(root,
! aggtarget,
! aggsortop,
! false, /* nulls_first */
! 0,
! true,
! false);
return list_make1(pathkey);
}
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 908b4f7205d86f0888007d3a05e65b13132520b9..a29401ba48c7776f91fdb38bbc1100801661327b 100644
*** a/src/backend/optimizer/util/plancat.c
--- b/src/backend/optimizer/util/plancat.c
*************** get_relation_info(PlannerInfo *root, Oid
*** 189,207 ****
RelationGetForm(indexRelation)->reltablespace;
info->rel = rel;
info->ncolumns = ncolumns = index->indnatts;
-
- /*
- * Allocate per-column info arrays. To save a few palloc cycles
- * we allocate all the Oid-type arrays in one request. We must
- * pre-zero the sortop and nulls_first arrays in case the index is
- * unordered.
- */
info->indexkeys = (int *) palloc(sizeof(int) * ncolumns);
! info->opfamily = (Oid *) palloc0(sizeof(Oid) * (4 * ncolumns));
! info->opcintype = info->opfamily + ncolumns;
! info->fwdsortop = info->opcintype + ncolumns;
! info->revsortop = info->fwdsortop + ncolumns;
! info->nulls_first = (bool *) palloc0(sizeof(bool) * ncolumns);
for (i = 0; i < ncolumns; i++)
{
--- 189,197 ----
RelationGetForm(indexRelation)->reltablespace;
info->rel = rel;
info->ncolumns = ncolumns = index->indnatts;
info->indexkeys = (int *) palloc(sizeof(int) * ncolumns);
! info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns);
! info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns);
for (i = 0; i < ncolumns; i++)
{
*************** get_relation_info(PlannerInfo *root, Oid
*** 219,267 ****
info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap);
/*
! * Fetch the ordering operators associated with the index, if any.
! * We expect that all ordering-capable indexes use btree's
! * strategy numbers for the ordering operators.
*/
if (indexRelation->rd_am->amcanorder)
{
! int nstrat = indexRelation->rd_am->amstrategies;
for (i = 0; i < ncolumns; i++)
{
int16 opt = indexRelation->rd_indoption[i];
- int fwdstrat;
- int revstrat;
! if (opt & INDOPTION_DESC)
! {
! fwdstrat = BTGreaterStrategyNumber;
! revstrat = BTLessStrategyNumber;
! }
! else
! {
! fwdstrat = BTLessStrategyNumber;
! revstrat = BTGreaterStrategyNumber;
! }
!
! /*
! * Index AM must have a fixed set of strategies for it to
! * make sense to specify amcanorder, so we need not allow
! * the case amstrategies == 0.
! */
! if (fwdstrat > 0)
! {
! Assert(fwdstrat <= nstrat);
! info->fwdsortop[i] = indexRelation->rd_operator[i * nstrat + fwdstrat - 1];
! }
! if (revstrat > 0)
! {
! Assert(revstrat <= nstrat);
! info->revsortop[i] = indexRelation->rd_operator[i * nstrat + revstrat - 1];
! }
info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
}
}
/*
* Fetch the index expressions and predicate, if any. We must
--- 209,244 ----
info->amhasgetbitmap = OidIsValid(indexRelation->rd_am->amgetbitmap);
/*
! * Fetch the ordering information for the index, if any.
! *
! * Currently, we assume that any orderable index uses btree's
! * opfamily numbers, which really is equivalent to supposing that
! * btree is the only amcanorder index type. To support other
! * orderable index types, we'd need a way to map from their
! * opfamilies to btree opfamilies. (This could reasonably be
! * done as an additional property in pg_opfamily, but for the
! * foreseeable future there's no need to bother.)
*/
if (indexRelation->rd_am->amcanorder)
{
! info->sortopfamily = info->opfamily;
! info->reverse_sort = (bool *) palloc(sizeof(bool) * ncolumns);
! info->nulls_first = (bool *) palloc(sizeof(bool) * ncolumns);
for (i = 0; i < ncolumns; i++)
{
int16 opt = indexRelation->rd_indoption[i];
! info->reverse_sort[i] = (opt & INDOPTION_DESC) != 0;
info->nulls_first[i] = (opt & INDOPTION_NULLS_FIRST) != 0;
}
}
+ else
+ {
+ info->sortopfamily = NULL;
+ info->reverse_sort = NULL;
+ info->nulls_first = NULL;
+ }
/*
* Fetch the index expressions and predicate, if any. We must
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c7442218a84a12972658ef320c20c7aa783b80a7..95397aa7cee3132db72138443bd7181ff5c32889 100644
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
*************** get_actual_variable_range(PlannerInfo *r
*** 4567,4580 ****
* The first index column must match the desired variable and sort
* operator --- but we can use a descending-order index.
*/
- if (sortop == index->fwdsortop[0])
- indexscandir = ForwardScanDirection;
- else if (sortop == index->revsortop[0])
- indexscandir = BackwardScanDirection;
- else
- continue;
if (!match_index_to_operand(vardata->var, 0, index))
continue;
/*
* Found a suitable index to extract data from. We'll need an EState
--- 4567,4592 ----
* The first index column must match the desired variable and sort
* operator --- but we can use a descending-order index.
*/
if (!match_index_to_operand(vardata->var, 0, index))
continue;
+ switch (get_op_opfamily_strategy(sortop, index->sortopfamily[0]))
+ {
+ case BTLessStrategyNumber:
+ if (index->reverse_sort[0])
+ indexscandir = BackwardScanDirection;
+ else
+ indexscandir = ForwardScanDirection;
+ break;
+ case BTGreaterStrategyNumber:
+ if (index->reverse_sort[0])
+ indexscandir = ForwardScanDirection;
+ else
+ indexscandir = BackwardScanDirection;
+ break;
+ default:
+ /* index doesn't match the sortop */
+ continue;
+ }
/*
* Found a suitable index to extract data from. We'll need an EState
*************** btcostestimate(PG_FUNCTION_ARGS)
*** 6150,6161 ****
if (HeapTupleIsValid(vardata.statsTuple))
{
float4 *numbers;
int nnumbers;
! if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
STATISTIC_KIND_CORRELATION,
! index->fwdsortop[0],
NULL,
NULL, NULL,
&numbers, &nnumbers))
--- 6162,6179 ----
if (HeapTupleIsValid(vardata.statsTuple))
{
+ Oid sortop;
float4 *numbers;
int nnumbers;
! sortop = get_opfamily_member(index->opfamily[0],
! index->opcintype[0],
! index->opcintype[0],
! BTLessStrategyNumber);
! if (OidIsValid(sortop) &&
! get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
STATISTIC_KIND_CORRELATION,
! sortop,
NULL,
NULL, NULL,
&numbers, &nnumbers))
*************** btcostestimate(PG_FUNCTION_ARGS)
*** 6165,6170 ****
--- 6183,6191 ----
Assert(nnumbers == 1);
varCorrelation = numbers[0];
+ if (index->reverse_sort[0])
+ varCorrelation = -varCorrelation;
+
if (index->ncolumns > 1)
*indexCorrelation = varCorrelation * 0.75;
else
*************** btcostestimate(PG_FUNCTION_ARGS)
*** 6172,6196 ****
free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
}
- else if (get_attstatsslot(vardata.statsTuple, InvalidOid, 0,
- STATISTIC_KIND_CORRELATION,
- index->revsortop[0],
- NULL,
- NULL, NULL,
- &numbers, &nnumbers))
- {
- double varCorrelation;
-
- Assert(nnumbers == 1);
- varCorrelation = numbers[0];
-
- if (index->ncolumns > 1)
- *indexCorrelation = -varCorrelation * 0.75;
- else
- *indexCorrelation = -varCorrelation;
-
- free_attstatsslot(InvalidOid, NULL, 0, numbers, nnumbers);
- }
}
ReleaseVariableStats(vardata);
--- 6193,6198 ----
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 785acc955ad652254c547d015715ec6ac1841925..d084338f356206c354a0a85179ea358862eab5bb 100644
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
*************** typedef struct RelOptInfo
*** 421,440 ****
* IndexOptInfo
* Per-index information for planning/optimization
*
! * Prior to Postgres 7.0, RelOptInfo was used to describe both relations
! * and indexes, but that created confusion without actually doing anything
! * useful. So now we have a separate IndexOptInfo struct for indexes.
! *
! * opfamily[], indexkeys[], opcintype[], fwdsortop[], revsortop[],
! * and nulls_first[] each have ncolumns entries.
*
* Zeroes in the indexkeys[] array indicate index columns that are
* expressions; there is one element in indexprs for each such column.
*
! * For an unordered index, the sortop arrays contains zeroes. Note that
! * fwdsortop[] and nulls_first[] describe the sort ordering of a forward
! * indexscan; we can also consider a backward indexscan, which will
! * generate sort order described by revsortop/!nulls_first.
*
* The indexprs and indpred expressions have been run through
* prepqual.c and eval_const_expressions() for ease of matching to
--- 421,437 ----
* IndexOptInfo
* Per-index information for planning/optimization
*
! * opfamily[], indexkeys[], and opcintype[] each have ncolumns entries.
! * sortopfamily[], reverse_sort[], and nulls_first[] likewise have
! * ncolumns entries, if the index is ordered; but if it is unordered,
! * those pointers are NULL.
*
* Zeroes in the indexkeys[] array indicate index columns that are
* expressions; there is one element in indexprs for each such column.
*
! * For an ordered index, reverse_sort[] and nulls_first[] describe the
! * sort ordering of a forward indexscan; we can also consider a backward
! * indexscan, which will generate the reverse ordering.
*
* The indexprs and indpred expressions have been run through
* prepqual.c and eval_const_expressions() for ease of matching to
*************** typedef struct IndexOptInfo
*** 457,464 ****
Oid *opfamily; /* OIDs of operator families for columns */
int *indexkeys; /* column numbers of index's keys, or 0 */
Oid *opcintype; /* OIDs of opclass declared input data types */
! Oid *fwdsortop; /* OIDs of sort operators for each column */
! Oid *revsortop; /* OIDs of sort operators for backward scan */
bool *nulls_first; /* do NULLs come first in the sort order? */
Oid relam; /* OID of the access method (in pg_am) */
--- 454,461 ----
Oid *opfamily; /* OIDs of operator families for columns */
int *indexkeys; /* column numbers of index's keys, or 0 */
Oid *opcintype; /* OIDs of opclass declared input data types */
! Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */
! bool *reverse_sort; /* is sort order descending? */
bool *nulls_first; /* do NULLs come first in the sort order? */
Oid relam; /* OID of the access method (in pg_am) */