|
12 | 12 |
|
13 | 13 | #include "pathman.h"
|
14 | 14 |
|
| 15 | +#include "catalog/pg_collation.h" |
| 16 | +#include "miscadmin.h" |
| 17 | +#include "nodes/nodeFuncs.h" |
15 | 18 | #include "optimizer/clauses.h"
|
16 | 19 | #include "optimizer/cost.h"
|
17 | 20 | #include "optimizer/restrictinfo.h"
|
18 | 21 | #include "optimizer/planmain.h"
|
19 | 22 | #include "optimizer/tlist.h"
|
20 | 23 | #include "optimizer/var.h"
|
21 |
| -#include "miscadmin.h" |
| 24 | +#include "utils/builtins.h" |
22 | 25 | #include "utils/lsyscache.h"
|
23 | 26 | #include "utils/memutils.h"
|
| 27 | +#include "utils/ruleutils.h" |
24 | 28 |
|
25 | 29 | #include "lib/binaryheap.h"
|
26 | 30 |
|
@@ -53,6 +57,11 @@ static Sort * make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
|
53 | 57 |
|
54 | 58 | static void copy_plan_costsize(Plan *dest, Plan *src);
|
55 | 59 |
|
| 60 | +static void show_sort_group_keys(PlanState *planstate, const char *qlabel, |
| 61 | + int nkeys, AttrNumber *keycols, |
| 62 | + Oid *sortOperators, Oid *collations, bool *nullsFirst, |
| 63 | + List *ancestors, ExplainState *es); |
| 64 | + |
56 | 65 | /*
|
57 | 66 | * We have one slot for each item in the heap array. We use SlotNumber
|
58 | 67 | * to store slot indexes. This doesn't actually provide any formal
|
@@ -443,6 +452,12 @@ runtimemergeappend_explain(CustomScanState *node, List *ancestors, ExplainState
|
443 | 452 | RuntimeMergeAppendState *scan_state = (RuntimeMergeAppendState *) node;
|
444 | 453 |
|
445 | 454 | explain_append_common(node, scan_state->rstate.children_table, es);
|
| 455 | + |
| 456 | + /* We should print sort keys as well */ |
| 457 | + show_sort_group_keys((PlanState *) &node->ss.ps, "Sort Key", |
| 458 | + scan_state->numCols, scan_state->sortColIdx, |
| 459 | + scan_state->sortOperators, scan_state->collations, |
| 460 | + scan_state->nullsFirst, ancestors, es); |
446 | 461 | }
|
447 | 462 |
|
448 | 463 |
|
@@ -765,3 +780,118 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
|
765 | 780 |
|
766 | 781 | return lefttree;
|
767 | 782 | }
|
| 783 | + |
| 784 | +/* |
| 785 | + * Append nondefault characteristics of the sort ordering of a column to buf |
| 786 | + * (collation, direction, NULLS FIRST/LAST) |
| 787 | + */ |
| 788 | +static void |
| 789 | +show_sortorder_options(StringInfo buf, Node *sortexpr, |
| 790 | + Oid sortOperator, Oid collation, bool nullsFirst) |
| 791 | +{ |
| 792 | + Oid sortcoltype = exprType(sortexpr); |
| 793 | + bool reverse = false; |
| 794 | + TypeCacheEntry *typentry; |
| 795 | + |
| 796 | + typentry = lookup_type_cache(sortcoltype, |
| 797 | + TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); |
| 798 | + |
| 799 | + /* |
| 800 | + * Print COLLATE if it's not default. There are some cases where this is |
| 801 | + * redundant, eg if expression is a column whose declared collation is |
| 802 | + * that collation, but it's hard to distinguish that here. |
| 803 | + */ |
| 804 | + if (OidIsValid(collation) && collation != DEFAULT_COLLATION_OID) |
| 805 | + { |
| 806 | + char *collname = get_collation_name(collation); |
| 807 | + |
| 808 | + if (collname == NULL) |
| 809 | + elog(ERROR, "cache lookup failed for collation %u", collation); |
| 810 | + appendStringInfo(buf, " COLLATE %s", quote_identifier(collname)); |
| 811 | + } |
| 812 | + |
| 813 | + /* Print direction if not ASC, or USING if non-default sort operator */ |
| 814 | + if (sortOperator == typentry->gt_opr) |
| 815 | + { |
| 816 | + appendStringInfoString(buf, " DESC"); |
| 817 | + reverse = true; |
| 818 | + } |
| 819 | + else if (sortOperator != typentry->lt_opr) |
| 820 | + { |
| 821 | + char *opname = get_opname(sortOperator); |
| 822 | + |
| 823 | + if (opname == NULL) |
| 824 | + elog(ERROR, "cache lookup failed for operator %u", sortOperator); |
| 825 | + appendStringInfo(buf, " USING %s", opname); |
| 826 | + /* Determine whether operator would be considered ASC or DESC */ |
| 827 | + (void) get_equality_op_for_ordering_op(sortOperator, &reverse); |
| 828 | + } |
| 829 | + |
| 830 | + /* Add NULLS FIRST/LAST only if it wouldn't be default */ |
| 831 | + if (nullsFirst && !reverse) |
| 832 | + { |
| 833 | + appendStringInfoString(buf, " NULLS FIRST"); |
| 834 | + } |
| 835 | + else if (!nullsFirst && reverse) |
| 836 | + { |
| 837 | + appendStringInfoString(buf, " NULLS LAST"); |
| 838 | + } |
| 839 | +} |
| 840 | + |
| 841 | +/* |
| 842 | + * Common code to show sort/group keys, which are represented in plan nodes |
| 843 | + * as arrays of targetlist indexes. If it's a sort key rather than a group |
| 844 | + * key, also pass sort operators/collations/nullsFirst arrays. |
| 845 | + */ |
| 846 | +static void |
| 847 | +show_sort_group_keys(PlanState *planstate, const char *qlabel, |
| 848 | + int nkeys, AttrNumber *keycols, |
| 849 | + Oid *sortOperators, Oid *collations, bool *nullsFirst, |
| 850 | + List *ancestors, ExplainState *es) |
| 851 | +{ |
| 852 | + Plan *plan = planstate->plan; |
| 853 | + List *context; |
| 854 | + List *result = NIL; |
| 855 | + StringInfoData sortkeybuf; |
| 856 | + bool useprefix; |
| 857 | + int keyno; |
| 858 | + |
| 859 | + if (nkeys <= 0) |
| 860 | + return; |
| 861 | + |
| 862 | + initStringInfo(&sortkeybuf); |
| 863 | + |
| 864 | + /* Set up deparsing context */ |
| 865 | + context = set_deparse_context_planstate(es->deparse_cxt, |
| 866 | + (Node *) planstate, |
| 867 | + ancestors); |
| 868 | + useprefix = (list_length(es->rtable) > 1 || es->verbose); |
| 869 | + |
| 870 | + for (keyno = 0; keyno < nkeys; keyno++) |
| 871 | + { |
| 872 | + /* find key expression in tlist */ |
| 873 | + AttrNumber keyresno = keycols[keyno]; |
| 874 | + TargetEntry *target = get_tle_by_resno(plan->targetlist, |
| 875 | + keyresno); |
| 876 | + char *exprstr; |
| 877 | + |
| 878 | + if (!target) |
| 879 | + elog(ERROR, "no tlist entry for key %d", keyresno); |
| 880 | + /* Deparse the expression, showing any top-level cast */ |
| 881 | + exprstr = deparse_expression((Node *) target->expr, context, |
| 882 | + useprefix, true); |
| 883 | + resetStringInfo(&sortkeybuf); |
| 884 | + appendStringInfoString(&sortkeybuf, exprstr); |
| 885 | + /* Append sort order information, if relevant */ |
| 886 | + if (sortOperators != NULL) |
| 887 | + show_sortorder_options(&sortkeybuf, |
| 888 | + (Node *) target->expr, |
| 889 | + sortOperators[keyno], |
| 890 | + collations[keyno], |
| 891 | + nullsFirst[keyno]); |
| 892 | + /* Emit one property-list item per sort key */ |
| 893 | + result = lappend(result, pstrdup(sortkeybuf.data)); |
| 894 | + } |
| 895 | + |
| 896 | + ExplainPropertyList(qlabel, result, es); |
| 897 | +} |
0 commit comments