Clean up planner confusion between ncolumns and nkeycolumns.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Feb 2019 23:38:32 +0000 (18:38 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 12 Feb 2019 23:38:32 +0000 (18:38 -0500)
We're only going to consider key columns when creating indexquals,
so there is no point in having the outer loops in indxpath.c iterate
further than nkeycolumns.

Doing so in match_pathkeys_to_index() is actually wrong, and would have
caused crashes by now, except that we have no index AMs supporting both
amcanorderbyop and amcaninclude.

It's also wrong in relation_has_unique_index_for().  The effect there is
to fail to prove uniqueness even when the index does prove it, if there
are extra columns.

Also future-proof examine_variable() for the day when extra columns can
be expressions, and fix what's either a thinko or just an oversight in
btcostestimate(): we should consider the number of key columns, not the
total, when deciding whether to derate correlation.

None of these things seemed important enough to risk changing in a
just-before-wrap patch, but since we're past the release wrap window,
time to fix 'em.

Discussion: https://postgr.es/m/25526.1549847928@sss.pgh.pa.us

src/backend/optimizer/path/indxpath.c
src/backend/utils/adt/selfuncs.c

index 2d5a09b1b458c7e50599df1a5b338c836509e7b0..e0933fc24d0c7cdaee46b0c5f18d404b39a2f66e 100644 (file)
@@ -253,7 +253,7 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
        IndexOptInfo *index = (IndexOptInfo *) lfirst(lc);
 
        /* Protect limited-size array in IndexClauseSets */
-       Assert(index->ncolumns <= INDEX_MAX_KEYS);
+       Assert(index->nkeycolumns <= INDEX_MAX_KEYS);
 
        /*
         * Ignore partial indexes that do not match the query.
@@ -468,7 +468,7 @@ consider_index_join_clauses(PlannerInfo *root, RelOptInfo *rel,
     * relation itself is also included in the relids set.  considered_relids
     * lists all relids sets we've already tried.
     */
-   for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
+   for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
    {
        /* Consider each applicable simple join clause */
        considered_clauses += list_length(jclauseset->indexclauses[indexcol]);
@@ -623,7 +623,7 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
    /* Identify indexclauses usable with this relids set */
    MemSet(&clauseset, 0, sizeof(clauseset));
 
-   for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
+   for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
    {
        ListCell   *lc;
 
@@ -920,7 +920,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
    index_clauses = NIL;
    found_lower_saop_clause = false;
    outer_relids = bms_copy(rel->lateral_relids);
-   for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
+   for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
    {
        ListCell   *lc;
 
@@ -3237,11 +3237,12 @@ match_pathkeys_to_index(IndexOptInfo *index, List *pathkeys,
            /*
             * We allow any column of the index to match each pathkey; they
             * don't have to match left-to-right as you might expect.  This is
-            * correct for GiST, which is the sole existing AM supporting
-            * amcanorderbyop.  We might need different logic in future for
-            * other implementations.
+            * correct for GiST, and it doesn't matter for SP-GiST because
+            * that doesn't handle multiple columns anyway, and no other
+            * existing AMs support amcanorderbyop.  We might need different
+            * logic in future for other implementations.
             */
-           for (indexcol = 0; indexcol < index->ncolumns; indexcol++)
+           for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++)
            {
                Expr       *expr;
 
@@ -3672,7 +3673,7 @@ relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel,
         * Try to find each index column in the lists of conditions.  This is
         * O(N^2) or worse, but we expect all the lists to be short.
         */
-       for (c = 0; c < ind->ncolumns; c++)
+       for (c = 0; c < ind->nkeycolumns; c++)
        {
            bool        matched = false;
            ListCell   *lc;
@@ -3747,8 +3748,8 @@ relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel,
                break;          /* no match; this index doesn't help us */
        }
 
-       /* Matched all columns of this index? */
-       if (c == ind->ncolumns)
+       /* Matched all key columns of this index? */
+       if (c == ind->nkeycolumns)
            return true;
    }
 
index 1ef6faecd1eed7ea0c0f245dd9d7305f0790ea14..b9f99fa050b6f150adbae6acaba7d863ecb1bf33 100644 (file)
@@ -4869,6 +4869,10 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
         * it to expressional index columns, in hopes of finding some
         * statistics.
         *
+        * Note that we consider all index columns including INCLUDE columns,
+        * since there could be stats for such columns.  But the test for
+        * uniqueness needs to be warier.
+        *
         * XXX it's conceivable that there are multiple matches with different
         * index opfamilies; if so, we need to pick one that matches the
         * operator we are estimating for.  FIXME later.
@@ -4904,6 +4908,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
                         */
                        if (index->unique &&
                            index->nkeycolumns == 1 &&
+                           pos == 0 &&
                            (index->indpred == NIL || index->predOK))
                            vardata->isunique = true;
 
@@ -7242,7 +7247,7 @@ btcostestimate(PlannerInfo *root, IndexPath *path, double loop_count,
            if (index->reverse_sort[0])
                varCorrelation = -varCorrelation;
 
-           if (index->ncolumns > 1)
+           if (index->nkeycolumns > 1)
                costs.indexCorrelation = varCorrelation * 0.75;
            else
                costs.indexCorrelation = varCorrelation;