88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.66 1999/07/27 03:51:01 tgl Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.67 1999/07/30 04:07:23 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -67,8 +67,7 @@ static void indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
6767List * * clausegroups ,List * * outerrelids );
6868static List * index_innerjoin (Query * root ,RelOptInfo * rel ,RelOptInfo * index ,
6969List * clausegroup_list ,List * outerrelids_list );
70- static List * create_index_path_group (Query * root ,RelOptInfo * rel ,RelOptInfo * index ,
71- List * clausegroup_list ,bool join );
70+ static bool useful_for_mergejoin (RelOptInfo * index ,List * clausegroup_list );
7271static bool match_index_to_operand (int indexkey ,Expr * operand ,
7372RelOptInfo * rel ,RelOptInfo * index );
7473static bool function_index_operand (Expr * funcOpnd ,RelOptInfo * rel ,RelOptInfo * index );
@@ -84,19 +83,40 @@ static List *prefix_quals(Var *leftop, Oid expr_op,
8483 * create_index_paths()
8584 * Generate all interesting index paths for the given relation.
8685 *
87- * To be considered for an index scan, an index must match one or more
88- * restriction clauses or join clauses from the query's qual condition.
86+ * To be considered for an index scan, an index must match one or more
87+ * restriction clauses or join clauses from the query's qual condition.
8988 *
90- * Note: an index scan might also be used simply to order the result,
91- * either for use in a mergejoin or to satisfy an ORDER BY request.
92- * That possibility is handled elsewhere.
89+ * There are two basic kinds of index scans. A "plain" index scan uses
90+ * only restriction clauses (possibly none at all) in its indexqual,
91+ * so it can be applied in any context. An "innerjoin" index scan uses
92+ * join clauses (plus restriction clauses, if available) in its indexqual.
93+ * Therefore it can only be used as the inner relation of a nestloop
94+ * join against an outer rel that includes all the other rels mentioned
95+ * in its join clauses. In that context, values for the other rels'
96+ * attributes are available and fixed during any one scan of the indexpath.
97+ *
98+ * This routine's return value is a list of plain IndexPaths for each
99+ * index the routine deems potentially interesting for the current query
100+ * (at most one IndexPath per index on the given relation). An innerjoin
101+ * path is also generated for each interesting combination of outer join
102+ * relations. The innerjoin paths are *not* in the return list, but are
103+ * appended to the "innerjoin" list of the relation itself.
104+ *
105+ * XXX An index scan might also be used simply to order the result. We
106+ * probably should create an index path for any index that matches the
107+ * query's ORDER BY condition, even if it doesn't seem useful for join
108+ * or restriction clauses. But currently, such a path would never
109+ * survive the path selection process, so there's no point. The selection
110+ * process needs to award bonus scores to indexscans that produce a
111+ * suitably-ordered result...
93112 *
94113 * 'rel' is the relation for which we want to generate index paths
95114 * 'indices' is a list of available indexes for 'rel'
96115 * 'restrictinfo_list' is a list of restrictinfo nodes for 'rel'
97116 * 'joininfo_list' is a list of joininfo nodes for 'rel'
98117 *
99- * Returns a list of IndexPath access path descriptors.
118+ * Returns a list of IndexPath access path descriptors. Additional
119+ * IndexPath nodes may also be added to the rel->innerjoin list.
100120 */
101121List *
102122create_index_paths (Query * root ,
@@ -111,7 +131,7 @@ create_index_paths(Query *root,
111131foreach (ilist ,indices )
112132{
113133RelOptInfo * index = (RelOptInfo * )lfirst (ilist );
114- List * scanclausegroups ;
134+ List * restrictclauses ;
115135List * joinclausegroups ;
116136List * joinouterrelids ;
117137
@@ -140,9 +160,8 @@ create_index_paths(Query *root,
140160 * of the index), but our poor brains are hurting already...
141161 *
142162 * We don't even think about special handling of 'or' clauses that
143- * involve more than one relation, since they can't be processed by
144- * a single indexscan path anyway. Currently, cnfify() is certain
145- * to have restructured any such toplevel 'or' clauses anyway.
163+ * involve more than one relation (ie, are join clauses).
164+ * Can we do anything useful with those?
146165 */
147166match_index_orclauses (rel ,
148167index ,
@@ -155,26 +174,33 @@ create_index_paths(Query *root,
155174 * restriction clauses, then create a path using those clauses
156175 * as indexquals.
157176 */
158- scanclausegroups = group_clauses_by_indexkey (rel ,
159- index ,
160- index -> indexkeys ,
161- index -> classlist ,
162- restrictinfo_list );
163-
164- if (scanclausegroups != NIL )
165- retval = nconc (retval ,
166- create_index_path_group (root ,
167- rel ,
168- index ,
169- scanclausegroups ,
170- false));
177+ restrictclauses = group_clauses_by_indexkey (rel ,
178+ index ,
179+ index -> indexkeys ,
180+ index -> classlist ,
181+ restrictinfo_list );
182+
183+ if (restrictclauses != NIL )
184+ retval = lappend (retval ,
185+ create_index_path (root ,rel ,index ,
186+ restrictclauses ));
171187
172188/*
173189 * 3. If this index can be used with any join clause, then create
174- * pathnodes for each group of usable clauses.An index can be
175- * used with a join clause if its ordering is useful for a
176- * mergejoin, or if the index can possibly be used for scanning
177- * the inner relation of a nestloop join.
190+ * an index path for it even if there were no restriction clauses.
191+ * (If there were, there is no need to make another index path.)
192+ * This will allow the index to be considered as a base for a
193+ * mergejoin in later processing.
194+ * Also, create an innerjoin index path for each combination of
195+ * other rels used in available join clauses. These paths will
196+ * be considered as the inner side of nestloop joins against
197+ * those sets of other rels.
198+ * indexable_joinclauses() finds clauses that are potentially
199+ * applicable to either case. useful_for_mergejoin() tests to
200+ * see whether any of the join clauses might support a mergejoin.
201+ * index_innerjoin() builds an innerjoin index path for each
202+ * potential set of outer rels, which we add to the rel's
203+ * innerjoin list.
178204 */
179205indexable_joinclauses (rel ,index ,
180206joininfo_list ,restrictinfo_list ,
@@ -183,12 +209,13 @@ create_index_paths(Query *root,
183209
184210if (joinclausegroups != NIL )
185211{
186- retval = nconc (retval ,
187- create_index_path_group (root ,
188- rel ,
189- index ,
190- joinclausegroups ,
191- true));
212+ /* no need to create a plain path if we already did */
213+ if (restrictclauses == NIL &&
214+ useful_for_mergejoin (index ,joinclausegroups ))
215+ retval = lappend (retval ,
216+ create_index_path (root ,rel ,index ,
217+ NIL ));
218+
192219rel -> innerjoin = nconc (rel -> innerjoin ,
193220index_innerjoin (root ,rel ,index ,
194221joinclausegroups ,
@@ -344,21 +371,18 @@ match_index_orclause(RelOptInfo *rel,
344371 * 'classes' are the classes of the index operators on those keys.
345372 * 'restrictinfo_list' is the list of available restriction clauses for 'rel'.
346373 *
347- * Returns NIL if no clauses can be used with this index.
348- * Otherwise, a list containing a single sublist is returned (indicating
349- * to create_index_path_group() that a single IndexPath should be created).
350- * The sublist contains the RestrictInfo nodes for all clauses that can be
374+ * Returns a list of all the RestrictInfo nodes for clauses that can be
351375 * used with this index.
352376 *
353- * Thesublist is ordered by index key (but as far as I can tell, this is
377+ * Thelist is ordered by index key (but as far as I can tell, this is
354378 * an implementation artifact of this routine, and is not depended on by
355379 * any user of the returned list --- tgl 7/99).
356380 *
357381 * Note that in a multi-key index, we stop if we find a key that cannot be
358382 * used with any clause. For example, given an index on (A,B,C), we might
359- * return (( C1 C2 C3 C4) ) if we find that clauses C1 and C2 use column A,
360- * clauses C3 and C4 use column B, and no clauses use column C. But if no
361- * clauses match B we will return (( C1 C2) ), whether or not there are
383+ * return (C1 C2 C3 C4) if we find that clauses C1 and C2 use column A,
384+ * clauses C3 and C4 use column B, and no clauses use column C. But if
385+ *no clauses match B we will return (C1 C2), whether or not there are
362386 * clauses matching column C, because the executor couldn't use them anyway.
363387 */
364388static List *
@@ -407,9 +431,7 @@ group_clauses_by_indexkey(RelOptInfo *rel,
407431}while (!DoneMatchingIndexKeys (indexkeys ,index ));
408432
409433/* clausegroup_list holds all matched clauses ordered by indexkeys */
410- if (clausegroup_list != NIL )
411- return lcons (clausegroup_list ,NIL );
412- return NIL ;
434+ return clausegroup_list ;
413435}
414436
415437/*
@@ -418,9 +440,9 @@ group_clauses_by_indexkey(RelOptInfo *rel,
418440 *
419441 * This is much like group_clauses_by_indexkey(), but we consider both
420442 * join and restriction clauses. For each indexkey in the index, we
421- * accept both join and restriction clauses that match it ( since both
443+ * accept both join and restriction clauses that match it, since both
422444 * will make useful indexquals if the index is being used to scan the
423- * inner side of a join) . But there must be at least one matching
445+ * inner side of anestloop join. But there must be at least one matching
424446 * join clause, or we return NIL indicating that this index isn't useful
425447 * for joining.
426448 */
@@ -486,22 +508,18 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
486508
487509}while (!DoneMatchingIndexKeys (indexkeys ,index ));
488510
489- /* clausegroup_list holds all matched clauses ordered by indexkeys */
490-
491- if (clausegroup_list != NIL )
511+ /*
512+ * if no join clause was matched then there ain't clauses for
513+ * joins at all.
514+ */
515+ if (!jfound )
492516{
493- /*
494- * if no join clause was matched then there ain't clauses for
495- * joins at all.
496- */
497- if (!jfound )
498- {
499- freeList (clausegroup_list );
500- return NIL ;
501- }
502- return lcons (clausegroup_list ,NIL );
517+ freeList (clausegroup_list );
518+ return NIL ;
503519}
504- return NIL ;
520+
521+ /* clausegroup_list holds all matched clauses ordered by indexkeys */
522+ return clausegroup_list ;
505523}
506524
507525
@@ -1150,41 +1168,22 @@ indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
11501168foreach (i ,joininfo_list )
11511169{
11521170JoinInfo * joininfo = (JoinInfo * )lfirst (i );
1153- List * clausegroups ;
1154-
1155- if (joininfo -> jinfo_restrictinfo == NIL )
1156- continue ;
1157- clausegroups = group_clauses_by_ikey_for_joins (rel ,
1158- index ,
1159- index -> indexkeys ,
1160- index -> classlist ,
1171+ List * clausegroup ;
1172+
1173+ clausegroup = group_clauses_by_ikey_for_joins (rel ,
1174+ index ,
1175+ index -> indexkeys ,
1176+ index -> classlist ,
11611177joininfo -> jinfo_restrictinfo ,
1162- restrictinfo_list );
1163-
1164- /*----------
1165- * This code knows that group_clauses_by_ikey_for_joins() returns
1166- * either NIL or a list containing a single sublist of clauses.
1167- * The line
1168- *cg_list = nconc(cg_list, clausegroups);
1169- * is better read as
1170- *cg_list = lappend(cg_list, lfirst(clausegroups));
1171- * That is, we are appending the only sublist returned by
1172- * group_clauses_by_ikey_for_joins() to the list of clause sublists
1173- * that this routine will return. By using nconc() we recycle
1174- * a cons cell that would be wasted ... whoever wrote this code
1175- * was too clever by half...
1176- *----------
1177- */
1178- if (clausegroups != NIL )
1178+ restrictinfo_list );
1179+
1180+ if (clausegroup != NIL )
11791181{
1180- cg_list = nconc (cg_list ,clausegroups );
1182+ cg_list = lappend (cg_list ,clausegroup );
11811183relid_list = lappend (relid_list ,joininfo -> unjoined_relids );
11821184}
11831185}
11841186
1185- /* Make sure above clever code didn't screw up */
1186- Assert (length (cg_list )== length (relid_list ));
1187-
11881187* clausegroups = cg_list ;
11891188* outerrelids = relid_list ;
11901189}
@@ -1200,7 +1199,7 @@ indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
12001199 *
12011200 * 'rel' is the relation for which 'index' is defined
12021201 * 'clausegroup_list' is a list of lists of restrictinfo nodes which can use
1203- * 'index' on their inner relation .
1202+ * 'index'. Each sublist refers to the same set of outer rels .
12041203 * 'outerrelids_list' is a list of the required outer rels for each group
12051204 * of join clauses.
12061205 *
@@ -1245,10 +1244,12 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
12451244 * therefore, both indexid and indexqual should be single-element
12461245 * lists.
12471246 */
1247+ Assert (length (index -> relids )== 1 );
12481248pathnode -> indexid = index -> relids ;
1249- pathnode -> indexkeys = index -> indexkeys ;
12501249pathnode -> indexqual = lcons (indexquals ,NIL );
12511250
1251+ pathnode -> indexkeys = index -> indexkeys ;
1252+
12521253/* joinid saves the rels needed on the outer side of the join */
12531254pathnode -> path .joinid = lfirst (outerrelids_list );
12541255
@@ -1268,59 +1269,38 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
12681269}
12691270
12701271/*
1271- * create_index_path_group
1272- * Creates a list of index path nodes for each group of clauses
1273- * (restriction or join) that can be used in conjunction with an index.
1274- *
1275- * 'rel' is the relation for which 'index' is defined
1276- * 'clausegroup_list' is the list of clause groups (lists of restrictinfo
1277- *nodes) grouped by mergejoinorder
1278- * 'join' is a flag indicating whether or not the clauses are join
1279- *clauses
1280- *
1281- * Returns a list of new index path nodes.
1272+ * useful_for_mergejoin
1273+ * Determine whether the given index can support a mergejoin based
1274+ * on any join clause within the given list. The clauses have already
1275+ * been found to be relevant to the index by indexable_joinclauses.
1276+ * We just need to check whether any are mergejoin material.
12821277 *
1278+ * 'index' is the index of interest.
1279+ * 'clausegroup_list' is a list of clause groups (sublists of restrictinfo
1280+ *nodes)
12831281 */
1284- static List *
1285- create_index_path_group (Query * root ,
1286- RelOptInfo * rel ,
1287- RelOptInfo * index ,
1288- List * clausegroup_list ,
1289- bool join )
1282+ static bool
1283+ useful_for_mergejoin (RelOptInfo * index ,
1284+ List * clausegroup_list )
12901285{
1291- List * path_list = NIL ;
12921286List * i ;
12931287
12941288foreach (i ,clausegroup_list )
12951289{
12961290List * clausegroup = lfirst (i );
1297- bool usable = true ;
1291+ List * j ;
12981292
1299- if ( join )
1293+ foreach ( j , clausegroup )
13001294{
1301- List * j ;
1295+ RestrictInfo * restrictinfo = ( RestrictInfo * ) lfirst ( j ) ;
13021296
1303- foreach (j ,clausegroup )
1304- {
1305- RestrictInfo * restrictinfo = (RestrictInfo * )lfirst (j );
1306- if (!(is_joinable ((Node * )restrictinfo -> clause )&&
1307- equal_path_merge_ordering (index -> ordering ,
1308- restrictinfo -> mergejoinorder )))
1309- {
1310- usable = false;
1311- break ;
1312- }
1313- }
1314- }
1315-
1316- if (usable )
1317- {
1318- path_list = lappend (path_list ,
1319- create_index_path (root ,rel ,index ,
1320- clausegroup ,join ));
1297+ if (is_joinable ((Node * )restrictinfo -> clause )&&
1298+ equal_path_merge_ordering (index -> ordering ,
1299+ restrictinfo -> mergejoinorder ))
1300+ return true;
13211301}
13221302}
1323- return path_list ;
1303+ return false ;
13241304}
13251305
13261306/****************************************************************************