1111 * Portions Copyright (c) 1994, Regents of the University of California
1212 *
1313 * IDENTIFICATION
14- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.46 2003/02/08 20:20:54 tgl Exp $
14+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.47 2003/02/15 20:12:40 tgl Exp $
1515 *
1616 *-------------------------------------------------------------------------
1717 */
@@ -366,6 +366,31 @@ canonicalize_pathkeys(Query *root, List *pathkeys)
366366return new_pathkeys ;
367367}
368368
369+
370+ /*
371+ * count_canonical_peers
372+ * Given a PathKeyItem, find the equi_key_list subset it is a member of,
373+ * if any. If so, return the number of other members of the set.
374+ * If not, return 0 (without actually adding it to our equi_key_list).
375+ *
376+ * This is a hack to support the rather bogus heuristics in
377+ * build_subquery_pathkeys.
378+ */
379+ static int
380+ count_canonical_peers (Query * root ,PathKeyItem * item )
381+ {
382+ List * cursetlink ;
383+
384+ foreach (cursetlink ,root -> equi_key_list )
385+ {
386+ List * curset = lfirst (cursetlink );
387+
388+ if (member (item ,curset ))
389+ return length (curset )- 1 ;
390+ }
391+ return 0 ;
392+ }
393+
369394/****************************************************************************
370395 *PATHKEY COMPARISONS
371396 ****************************************************************************/
@@ -597,6 +622,9 @@ get_cheapest_fractional_path_for_pathkeys(List *paths,
597622 *
598623 * If 'scandir' is BackwardScanDirection, attempt to build pathkeys
599624 * representing a backwards scan of the index.Return NIL if can't do it.
625+ *
626+ * We generate the full pathkeys list whether or not all are useful for the
627+ * current query. Caller should do truncate_useless_pathkeys().
600628 */
601629List *
602630build_index_pathkeys (Query * root ,
@@ -699,9 +727,10 @@ find_indexkey_var(Query *root, RelOptInfo *rel, AttrNumber varattno)
699727
700728foreach (temp ,rel -> targetlist )
701729{
702- Var * tle_var = get_expr ( lfirst (temp ));
730+ Var * tle_var = ( Var * ) (( TargetEntry * ) lfirst (temp ))-> expr ;
703731
704- if (IsA (tle_var ,Var )&& tle_var -> varattno == varattno )
732+ if (IsA (tle_var ,Var )&&
733+ tle_var -> varattno == varattno )
705734return tle_var ;
706735}
707736
@@ -714,6 +743,112 @@ find_indexkey_var(Query *root, RelOptInfo *rel, AttrNumber varattno)
714743return makeVar (relid ,varattno ,vartypeid ,type_mod ,0 );
715744}
716745
746+ /*
747+ * build_subquery_pathkeys
748+ * Build a pathkeys list that describes the ordering of a subquery's
749+ * result (in the terms of the outer query). The subquery must already
750+ * have been planned, so that its query_pathkeys field has been set.
751+ *
752+ * It is not necessary for caller to do truncate_useless_pathkeys(),
753+ * because we select keys in a way that takes usefulness of the keys into
754+ * account.
755+ */
756+ List *
757+ build_subquery_pathkeys (Query * root ,RelOptInfo * rel ,Query * subquery )
758+ {
759+ List * retval = NIL ;
760+ int retvallen = 0 ;
761+ int outer_query_keys = length (root -> query_pathkeys );
762+ List * l ;
763+
764+ foreach (l ,subquery -> query_pathkeys )
765+ {
766+ List * sub_pathkey = (List * )lfirst (l );
767+ List * j ;
768+ PathKeyItem * best_item = NULL ;
769+ int best_score = 0 ;
770+ List * cpathkey ;
771+
772+ /*
773+ * The sub_pathkey could contain multiple elements (representing
774+ * knowledge that multiple items are effectively equal). Each
775+ * element might match none, one, or more of the output columns
776+ * that are visible to the outer query. This means we may have
777+ * multiple possible representations of the sub_pathkey in the
778+ * context of the outer query. Ideally we would generate them all
779+ * and put them all into a pathkey list of the outer query, thereby
780+ * propagating equality knowledge up to the outer query. Right now
781+ * we cannot do so, because the outer query's canonical pathkey
782+ * sets are already frozen when this is called. Instead we prefer
783+ * the one that has the highest "score" (number of canonical pathkey
784+ * peers, plus one if it matches the outer query_pathkeys).
785+ * This is the most likely to be useful in the outer query.
786+ */
787+ foreach (j ,sub_pathkey )
788+ {
789+ PathKeyItem * sub_item = (PathKeyItem * )lfirst (j );
790+ Node * sub_key = sub_item -> key ;
791+ List * k ;
792+
793+ foreach (k ,subquery -> targetList )
794+ {
795+ TargetEntry * tle = (TargetEntry * )lfirst (k );
796+
797+ if (!tle -> resdom -> resjunk &&
798+ equal (tle -> expr ,sub_key ))
799+ {
800+ /* Found a representation for this sub_key */
801+ Var * outer_var ;
802+ PathKeyItem * outer_item ;
803+ int score ;
804+
805+ outer_var = makeVar (rel -> relid ,
806+ tle -> resdom -> resno ,
807+ tle -> resdom -> restype ,
808+ tle -> resdom -> restypmod ,
809+ 0 );
810+ outer_item = makePathKeyItem ((Node * )outer_var ,
811+ sub_item -> sortop );
812+ /* score = # of mergejoin peers */
813+ score = count_canonical_peers (root ,outer_item );
814+ /* +1 if it matches the proper query_pathkeys item */
815+ if (retvallen < outer_query_keys &&
816+ member (outer_item ,
817+ nth (retvallen ,root -> query_pathkeys )))
818+ score ++ ;
819+ if (score > best_score )
820+ {
821+ best_item = outer_item ;
822+ best_score = score ;
823+ }
824+ }
825+ }
826+ }
827+
828+ /*
829+ * If we couldn't find a representation of this sub_pathkey,
830+ * we're done (we can't use the ones to its right, either).
831+ */
832+ if (!best_item )
833+ break ;
834+
835+ /* Canonicalize the chosen item (we did not before) */
836+ cpathkey = make_canonical_pathkey (root ,best_item );
837+
838+ /*
839+ * Eliminate redundant ordering info; could happen if outer
840+ * query equijoins subquery keys...
841+ */
842+ if (!ptrMember (cpathkey ,retval ))
843+ {
844+ retval = lappend (retval ,cpathkey );
845+ retvallen ++ ;
846+ }
847+ }
848+
849+ return retval ;
850+ }
851+
717852/*
718853 * build_join_pathkeys
719854 * Build the path keys for a join relation constructed by mergejoin or