|
11 | 11 | * Portions Copyright (c) 1994, Regents of the University of California
|
12 | 12 | *
|
13 | 13 | * IDENTIFICATION
|
14 |
| - * $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.88 2007/11/08 19:25:37 tgl Exp $ |
| 14 | + * $PostgreSQL: pgsql/src/backend/optimizer/path/pathkeys.c,v 1.89 2007/11/08 21:49:47 tgl Exp $ |
15 | 15 | *
|
16 | 16 | *-------------------------------------------------------------------------
|
17 | 17 | */
|
@@ -46,6 +46,7 @@ static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
|
46 | 46 | static PathKey *make_pathkey_from_sortinfo(PlannerInfo *root,
|
47 | 47 | Expr *expr, Oid ordering_op,
|
48 | 48 | bool nulls_first,
|
| 49 | + Index sortref, |
49 | 50 | bool canonicalize);
|
50 | 51 | static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
|
51 | 52 | AttrNumber varattno);
|
@@ -233,13 +234,17 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys)
|
233 | 234 | * a PathKey. If canonicalize = true, the result is a "canonical"
|
234 | 235 | * PathKey, otherwise not. (But note it might be redundant anyway.)
|
235 | 236 | *
|
| 237 | + * If the PathKey is being generated from a SortClause, sortref should be |
| 238 | + * the SortClause's SortGroupRef; otherwise zero. |
| 239 | + * |
236 | 240 | * canonicalize should always be TRUE after EquivalenceClass merging has
|
237 | 241 | * been performed, but FALSE if we haven't done EquivalenceClass merging yet.
|
238 | 242 | */
|
239 | 243 | static PathKey *
|
240 | 244 | make_pathkey_from_sortinfo(PlannerInfo *root,
|
241 | 245 | Expr *expr, Oid ordering_op,
|
242 | 246 | bool nulls_first,
|
| 247 | + Index sortref, |
243 | 248 | bool canonicalize)
|
244 | 249 | {
|
245 | 250 | Oid opfamily,
|
@@ -303,7 +308,8 @@ make_pathkey_from_sortinfo(PlannerInfo *root,
|
303 | 308 | }
|
304 | 309 |
|
305 | 310 | /* Now find or create a matching EquivalenceClass */
|
306 |
| - eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies); |
| 311 | + eclass = get_eclass_for_sort_expr(root, expr, opcintype, opfamilies, |
| 312 | + sortref); |
307 | 313 |
|
308 | 314 | /* And finally we can find or create a PathKey node */
|
309 | 315 | if (canonicalize)
|
@@ -525,6 +531,7 @@ build_index_pathkeys(PlannerInfo *root,
|
525 | 531 | indexkey,
|
526 | 532 | sortop,
|
527 | 533 | nulls_first,
|
| 534 | + 0, |
528 | 535 | true);
|
529 | 536 |
|
530 | 537 | /* Add to list unless redundant */
|
@@ -597,102 +604,161 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel,
|
597 | 604 | PathKey *sub_pathkey = (PathKey *) lfirst(i);
|
598 | 605 | EquivalenceClass *sub_eclass = sub_pathkey->pk_eclass;
|
599 | 606 | PathKey *best_pathkey = NULL;
|
600 |
| - int best_score = -1; |
601 |
| - ListCell *j; |
602 | 607 |
|
603 |
| - /* |
604 |
| - * The sub_pathkey's EquivalenceClass could contain multiple elements |
605 |
| - * (representing knowledge that multiple items are effectively equal). |
606 |
| - * Each element might match none, one, or more of the output columns |
607 |
| - * that are visible to the outer query. This means we may have |
608 |
| - * multiple possible representations of the sub_pathkey in the context |
609 |
| - * of the outer query. Ideally we would generate them all and put |
610 |
| - * them all into an EC of the outer query, thereby propagating |
611 |
| - * equality knowledge up to the outer query. Right now we cannot do |
612 |
| - * so, because the outer query's EquivalenceClasses are already frozen |
613 |
| - * when this is called. Instead we prefer the one that has the highest |
614 |
| - * "score" (number of EC peers, plus one if it matches the outer |
615 |
| - * query_pathkeys). This is the most likely to be useful in the outer |
616 |
| - * query. |
617 |
| - */ |
618 |
| - foreach(j, sub_eclass->ec_members) |
| 608 | + if (sub_eclass->ec_has_volatile) |
619 | 609 | {
|
620 |
| - EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j); |
621 |
| - Expr *sub_expr = sub_member->em_expr; |
622 |
| - Expr *rtarg; |
623 |
| - ListCell *k; |
624 |
| - |
625 | 610 | /*
|
626 |
| - * We handle two cases: the sub_pathkey key can be either an exact |
627 |
| - * match for a targetlist entry, or a RelabelType of a targetlist |
628 |
| - * entry. (The latter case is worth extra code because it arises |
629 |
| - * frequently in connection with varchar fields.) |
| 611 | + * If the sub_pathkey's EquivalenceClass is volatile, then it must |
| 612 | + * have come from an ORDER BY clause, and we have to match it to |
| 613 | + * that same targetlist entry. |
630 | 614 | */
|
631 |
| - if (IsA(sub_expr, RelabelType)) |
632 |
| - rtarg = ((RelabelType *) sub_expr)->arg; |
633 |
| - else |
634 |
| - rtarg = NULL; |
635 |
| - |
636 |
| - foreach(k, sub_tlist) |
| 615 | + TargetEntry *tle; |
| 616 | + |
| 617 | + if (sub_eclass->ec_sortref == 0) /* can't happen */ |
| 618 | + elog(ERROR, "volatile EquivalenceClass has no sortref"); |
| 619 | + tle = get_sortgroupref_tle(sub_eclass->ec_sortref, sub_tlist); |
| 620 | + Assert(tle); |
| 621 | + /* resjunk items aren't visible to outer query */ |
| 622 | + if (!tle->resjunk) |
637 | 623 | {
|
638 |
| - TargetEntry *tle = (TargetEntry *) lfirst(k); |
| 624 | + /* We can represent this sub_pathkey */ |
| 625 | + EquivalenceMember *sub_member; |
639 | 626 | Expr *outer_expr;
|
640 | 627 | EquivalenceClass *outer_ec;
|
641 |
| - PathKey *outer_pk; |
642 |
| - int score; |
643 | 628 |
|
644 |
| - /* resjunk items aren't visible to outer query */ |
645 |
| - if (tle->resjunk) |
646 |
| - continue; |
| 629 | + Assert(list_length(sub_eclass->ec_members) == 1); |
| 630 | + sub_member = (EquivalenceMember *) linitial(sub_eclass->ec_members); |
| 631 | + outer_expr = (Expr *) |
| 632 | + makeVar(rel->relid, |
| 633 | + tle->resno, |
| 634 | + exprType((Node *) tle->expr), |
| 635 | + exprTypmod((Node *) tle->expr), |
| 636 | + 0); |
| 637 | + outer_ec = |
| 638 | + get_eclass_for_sort_expr(root, |
| 639 | + outer_expr, |
| 640 | + sub_member->em_datatype, |
| 641 | + sub_eclass->ec_opfamilies, |
| 642 | + 0); |
| 643 | + best_pathkey = |
| 644 | + make_canonical_pathkey(root, |
| 645 | + outer_ec, |
| 646 | + sub_pathkey->pk_opfamily, |
| 647 | + sub_pathkey->pk_strategy, |
| 648 | + sub_pathkey->pk_nulls_first); |
| 649 | + } |
| 650 | + } |
| 651 | + else |
| 652 | + { |
| 653 | + /* |
| 654 | + * Otherwise, the sub_pathkey's EquivalenceClass could contain |
| 655 | + * multiple elements (representing knowledge that multiple items |
| 656 | + * are effectively equal). Each element might match none, one, or |
| 657 | + * more of the output columns that are visible to the outer |
| 658 | + * query. This means we may have multiple possible representations |
| 659 | + * of the sub_pathkey in the context of the outer query. Ideally |
| 660 | + * we would generate them all and put them all into an EC of the |
| 661 | + * outer query, thereby propagating equality knowledge up to the |
| 662 | + * outer query. Right now we cannot do so, because the outer |
| 663 | + * query's EquivalenceClasses are already frozen when this is |
| 664 | + * called. Instead we prefer the one that has the highest "score" |
| 665 | + * (number of EC peers, plus one if it matches the outer |
| 666 | + * query_pathkeys). This is the most likely to be useful in the |
| 667 | + * outer query. |
| 668 | + */ |
| 669 | + int best_score = -1; |
| 670 | + ListCell *j; |
647 | 671 |
|
648 |
| - if (equal(tle->expr, sub_expr)) |
649 |
| - { |
650 |
| - /* Exact match */ |
651 |
| - outer_expr = (Expr *) |
652 |
| - makeVar(rel->relid, |
653 |
| - tle->resno, |
654 |
| - exprType((Node *) tle->expr), |
655 |
| - exprTypmod((Node *) tle->expr), |
656 |
| - 0); |
657 |
| - } |
658 |
| - else if (rtarg && equal(tle->expr, rtarg)) |
| 672 | + foreach(j, sub_eclass->ec_members) |
| 673 | + { |
| 674 | + EquivalenceMember *sub_member = (EquivalenceMember *) lfirst(j); |
| 675 | + Expr *sub_expr = sub_member->em_expr; |
| 676 | + Expr *sub_stripped; |
| 677 | + ListCell *k; |
| 678 | + |
| 679 | + /* |
| 680 | + * We handle two cases: the sub_pathkey key can be either an |
| 681 | + * exact match for a targetlist entry, or it could match after |
| 682 | + * stripping RelabelType nodes. (We need that case since |
| 683 | + * make_pathkey_from_sortinfo could add or remove RelabelType.) |
| 684 | + */ |
| 685 | + sub_stripped = sub_expr; |
| 686 | + while (sub_stripped && IsA(sub_stripped, RelabelType)) |
| 687 | + sub_stripped = ((RelabelType *) sub_stripped)->arg; |
| 688 | + |
| 689 | + foreach(k, sub_tlist) |
659 | 690 | {
|
660 |
| - /* Match after discarding RelabelType */ |
661 |
| - outer_expr = (Expr *) |
662 |
| - makeVar(rel->relid, |
663 |
| - tle->resno, |
664 |
| - exprType((Node *) tle->expr), |
665 |
| - exprTypmod((Node *) tle->expr), |
666 |
| - 0); |
667 |
| - outer_expr = (Expr *) |
668 |
| - makeRelabelType((Expr *) outer_expr, |
669 |
| - ((RelabelType *) sub_expr)->resulttype, |
670 |
| - ((RelabelType *) sub_expr)->resulttypmod, |
671 |
| - ((RelabelType *) sub_expr)->relabelformat); |
672 |
| - } |
673 |
| - else |
674 |
| - continue; |
| 691 | + TargetEntry *tle = (TargetEntry *) lfirst(k); |
| 692 | + Expr *outer_expr; |
| 693 | + EquivalenceClass *outer_ec; |
| 694 | + PathKey *outer_pk; |
| 695 | + int score; |
675 | 696 |
|
676 |
| - /* Found a representation for this sub_pathkey */ |
677 |
| - outer_ec = get_eclass_for_sort_expr(root, |
678 |
| - outer_expr, |
679 |
| - sub_member->em_datatype, |
680 |
| - sub_eclass->ec_opfamilies); |
681 |
| - outer_pk = make_canonical_pathkey(root, |
682 |
| - outer_ec, |
683 |
| - sub_pathkey->pk_opfamily, |
684 |
| - sub_pathkey->pk_strategy, |
685 |
| - sub_pathkey->pk_nulls_first); |
686 |
| - /* score = # of equivalence peers */ |
687 |
| - score = list_length(outer_ec->ec_members) - 1; |
688 |
| - /* +1 if it matches the proper query_pathkeys item */ |
689 |
| - if (retvallen < outer_query_keys && |
690 |
| - list_nth(root->query_pathkeys, retvallen) == outer_pk) |
691 |
| - score++; |
692 |
| - if (score > best_score) |
693 |
| - { |
694 |
| - best_pathkey = outer_pk; |
695 |
| - best_score = score; |
| 697 | + /* resjunk items aren't visible to outer query */ |
| 698 | + if (tle->resjunk) |
| 699 | + continue; |
| 700 | + |
| 701 | + if (equal(tle->expr, sub_expr)) |
| 702 | + { |
| 703 | + /* Exact match */ |
| 704 | + outer_expr = (Expr *) |
| 705 | + makeVar(rel->relid, |
| 706 | + tle->resno, |
| 707 | + exprType((Node *) tle->expr), |
| 708 | + exprTypmod((Node *) tle->expr), |
| 709 | + 0); |
| 710 | + } |
| 711 | + else |
| 712 | + { |
| 713 | + Expr *tle_stripped; |
| 714 | + |
| 715 | + tle_stripped = tle->expr; |
| 716 | + while (tle_stripped && IsA(tle_stripped, RelabelType)) |
| 717 | + tle_stripped = ((RelabelType *) tle_stripped)->arg; |
| 718 | + |
| 719 | + if (equal(tle_stripped, sub_stripped)) |
| 720 | + { |
| 721 | + /* Match after discarding RelabelType */ |
| 722 | + outer_expr = (Expr *) |
| 723 | + makeVar(rel->relid, |
| 724 | + tle->resno, |
| 725 | + exprType((Node *) tle->expr), |
| 726 | + exprTypmod((Node *) tle->expr), |
| 727 | + 0); |
| 728 | + if (exprType((Node *) outer_expr) != |
| 729 | + exprType((Node *) sub_expr)) |
| 730 | + outer_expr = (Expr *) |
| 731 | + makeRelabelType(outer_expr, |
| 732 | + exprType((Node *) sub_expr), |
| 733 | + -1, |
| 734 | + COERCE_DONTCARE); |
| 735 | + } |
| 736 | + else |
| 737 | + continue; |
| 738 | + } |
| 739 | + |
| 740 | + /* Found a representation for this sub_pathkey */ |
| 741 | + outer_ec = get_eclass_for_sort_expr(root, |
| 742 | + outer_expr, |
| 743 | + sub_member->em_datatype, |
| 744 | + sub_eclass->ec_opfamilies, |
| 745 | + 0); |
| 746 | + outer_pk = make_canonical_pathkey(root, |
| 747 | + outer_ec, |
| 748 | + sub_pathkey->pk_opfamily, |
| 749 | + sub_pathkey->pk_strategy, |
| 750 | + sub_pathkey->pk_nulls_first); |
| 751 | + /* score = # of equivalence peers */ |
| 752 | + score = list_length(outer_ec->ec_members) - 1; |
| 753 | + /* +1 if it matches the proper query_pathkeys item */ |
| 754 | + if (retvallen < outer_query_keys && |
| 755 | + list_nth(root->query_pathkeys, retvallen) == outer_pk) |
| 756 | + score++; |
| 757 | + if (score > best_score) |
| 758 | + { |
| 759 | + best_pathkey = outer_pk; |
| 760 | + best_score = score; |
| 761 | + } |
696 | 762 | }
|
697 | 763 | }
|
698 | 764 | }
|
@@ -795,6 +861,7 @@ make_pathkeys_for_sortclauses(PlannerInfo *root,
|
795 | 861 | sortkey,
|
796 | 862 | sortcl->sortop,
|
797 | 863 | sortcl->nulls_first,
|
| 864 | + sortcl->tleSortGroupRef, |
798 | 865 | canonicalize);
|
799 | 866 |
|
800 | 867 | /* Canonical form eliminates redundant ordering keys */
|
@@ -843,12 +910,14 @@ cache_mergeclause_eclasses(PlannerInfo *root, RestrictInfo *restrictinfo)
|
843 | 910 | get_eclass_for_sort_expr(root,
|
844 | 911 | (Expr *) get_leftop(clause),
|
845 | 912 | lefttype,
|
846 |
| - restrictinfo->mergeopfamilies); |
| 913 | + restrictinfo->mergeopfamilies, |
| 914 | + 0); |
847 | 915 | restrictinfo->right_ec =
|
848 | 916 | get_eclass_for_sort_expr(root,
|
849 | 917 | (Expr *) get_rightop(clause),
|
850 | 918 | righttype,
|
851 |
| - restrictinfo->mergeopfamilies); |
| 919 | + restrictinfo->mergeopfamilies, |
| 920 | + 0); |
852 | 921 | }
|
853 | 922 | else
|
854 | 923 | Assert(restrictinfo->right_ec != NULL);
|
|
0 commit comments