# Sorry for accidentialy sending the previous mail unfinished. ## ...and I seem to have bombed uncertain files off out of my ## home directory by accident, too :(
===== Hi, sorry for the absense. I've been back. Thank you for continuing this discussion. Attached is the patch following the discussion below. > >> (2014/04/10 0:08), Tom Lane wrote: > >>> TBH I think that's barely the tip of the iceberg of cases where this > >>> patch will get the wrong answer. > > > >>> Also, I don't see it doing anything to check the ordering > >>> of multiple index columns > > > >> I think that the following code in index_pathkeys_are_extensible() would > >> check the ordering: > >> + if (!pathkeys_contained_in(pathkeys, root->query_pathkeys)) > >> + return false; > > > > Hm ... if you're relying on that, then what's the point of the new loop > > at all? > > The point is that from the discussion [1], we allow the index pathkeys > to be extended to query_pathkeys if each *remaining* pathkey in > query_pathkey is a Var belonging to the indexed relation. The code is > confusing, though. Sorry, that is my faults. Hmm, I found that the iterations for the part that already checked by pathkeys_contained_in are not only useless but a bit heavy. And the loop seems a little verbose. I did for the patch, in index_pathkeys_are_extensible, - Avoiding duplicate check with pathkeys_contained_in. I put similar code to list_nth_cell since it is not exposed outside of list.c. - Add comment to clarify the purpose of the loop. - Simplify the check for the "remaining" keycolumns I think this makes some things clearer. regards, -- Kyotaro Horiguchi NTT Open Source Software Center
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index bfb4b9f..ff5c88c 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1790,6 +1790,7 @@ _outIndexOptInfo(StringInfo str, const IndexOptInfo *node) WRITE_BOOL_FIELD(predOK); WRITE_BOOL_FIELD(unique); WRITE_BOOL_FIELD(immediate); + WRITE_BOOL_FIELD(allnotnull); WRITE_BOOL_FIELD(hypothetical); /* we don't bother with fields copied from the pg_am entry */ } diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index a912174..4376e95 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -951,8 +951,11 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, { index_pathkeys = build_index_pathkeys(root, index, ForwardScanDirection); - useful_pathkeys = truncate_useless_pathkeys(root, rel, - index_pathkeys); + if (index_pathkeys_are_extensible(root, index, index_pathkeys)) + useful_pathkeys = root->query_pathkeys; + else + useful_pathkeys = truncate_useless_pathkeys(root, rel, + index_pathkeys); orderbyclauses = NIL; orderbyclausecols = NIL; } diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 9179c61..5edca4f 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -502,6 +502,76 @@ build_index_pathkeys(PlannerInfo *root, } /* + * index_pathkeys_are_extensible + * Check whether the pathkeys are extensible to query_pathkeys. + */ +bool +index_pathkeys_are_extensible(PlannerInfo *root, + IndexOptInfo *index, + List *pathkeys) +{ + bool result; + ListCell *lc1, *remain; + + if (root->query_pathkeys == NIL || pathkeys == NIL) + return false; + + /* This index is not suitable for pathkey extension */ + if (!index->unique || !index->immediate || !index->allnotnull) + return false; + + /* pathkeys is a prefixing proper subset of index tlist */ + if (list_length(pathkeys) < list_length(index->indextlist)) + return false; + + if (!pathkeys_contained_in(pathkeys, root->query_pathkeys)) + return false; + + if (list_length(pathkeys) == list_length(root->query_pathkeys)) + return true; + + Assert(list_length(root->query_pathkeys) > list_length(pathkeys)); + + /* + * Check if all unchecked elements of query_patheys are simple Vars for + * the same relation. + */ + + /* list_nth_cell is not exposed publicly.. */ + if (list_length(pathkeys) == list_length(root->query_pathkeys) - 1) + remain = list_tail(root->query_pathkeys); + else + { + int n = list_length(pathkeys); + + remain = root->query_pathkeys->head; + while(n-- > 0) remain = remain->next; + } + + result = true; + for_each_cell(lc1, remain) + { + PathKey *pathkey = (PathKey *) lfirst(lc1); + ListCell *lc2; + + foreach(lc2, pathkey->pk_eclass->ec_members) + { + EquivalenceMember *member = (EquivalenceMember *) lfirst(lc2); + + if (!bms_equal(member->em_relids, index->rel->relids) || + !IsA(member->em_expr, Var)) + { + result = false; + break; + } + } + + if (!result) break; + } + return result; +} + +/* * build_expression_pathkey * Build a pathkeys list that describes an ordering by a single expression * using the given sort operator. diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 73ba2f6..c61cddb 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -333,6 +333,26 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, info->immediate = index->indimmediate; info->hypothetical = false; + info->allnotnull = true; + for (i = 0; i < ncolumns; i++) + { + int attrno = info->indexkeys[i]; + + if (attrno == 0) + { + info->allnotnull = false; + break; + } + else if (attrno > 0) + { + if (!relation->rd_att->attrs[attrno - 1]->attnotnull) + { + info->allnotnull = false; + break; + } + } + } + /* * Estimate the index size. If it's not a partial index, we lock * the number-of-tuples estimate to equal the parent table; if it diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index c607b36..119bb31 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -525,6 +525,7 @@ typedef struct IndexOptInfo bool predOK; /* true if predicate matches query */ bool unique; /* true if a unique index */ bool immediate; /* is uniqueness enforced immediately? */ + bool allnotnull; /* true if index's keys are all not null */ bool hypothetical; /* true if index doesn't really exist */ bool canreturn; /* can index return IndexTuples? */ bool amcanorderbyop; /* does AM support order by operator result? */ diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 9b22fda..8abdb99 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -187,5 +187,8 @@ extern List *truncate_useless_pathkeys(PlannerInfo *root, RelOptInfo *rel, List *pathkeys); extern bool has_useful_pathkeys(PlannerInfo *root, RelOptInfo *rel); +extern bool index_pathkeys_are_extensible(PlannerInfo *root, + IndexOptInfo *index, + List *pathkeys); #endif /* PATHS_H */ diff --git a/src/test/isolation/expected/drop-index-concurrently-1.out b/src/test/isolation/expected/drop-index-concurrently-1.out index 75dff56..ab96fa0 100644 --- a/src/test/isolation/expected/drop-index-concurrently-1.out +++ b/src/test/isolation/expected/drop-index-concurrently-1.out @@ -19,10 +19,8 @@ Sort step explains: EXPLAIN (COSTS OFF) EXECUTE getrow_seq; QUERY PLAN -Sort - Sort Key: id, data - -> Seq Scan on test_dc - Filter: ((data)::text = '34'::text) +Index Scan using test_dc_pkey on test_dc + Filter: ((data)::text = '34'::text) step select2: SELECT * FROM test_dc WHERE data=34 ORDER BY id,data; id data
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers