|
12 | 12 | #include "pathman.h"
|
13 | 13 |
|
14 | 14 | #include "postgres.h"
|
| 15 | +#include "catalog/pg_collation.h" |
| 16 | +#include "miscadmin.h" |
| 17 | +#include "nodes/nodeFuncs.h" |
| 18 | +#include "optimizer/clauses.h" |
15 | 19 | #include "optimizer/cost.h"
|
16 | 20 | #include "optimizer/planmain.h"
|
17 | 21 | #include "optimizer/tlist.h"
|
18 | 22 | #include "optimizer/var.h"
|
19 |
| -#include "miscadmin.h" |
| 23 | +#include "utils/builtins.h" |
| 24 | +#include "utils/guc.h" |
20 | 25 | #include "utils/lsyscache.h"
|
| 26 | +#include "utils/typcache.h" |
21 | 27 | #include "utils/memutils.h"
|
22 |
| -#include "utils/guc.h" |
| 28 | +#include "utils/ruleutils.h" |
| 29 | + |
23 | 30 | #include "lib/binaryheap.h"
|
24 | 31 |
|
25 | 32 |
|
@@ -51,6 +58,11 @@ static Sort * make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
|
51 | 58 |
|
52 | 59 | static void copy_plan_costsize(Plan *dest, Plan *src);
|
53 | 60 |
|
| 61 | +static void show_sort_group_keys(PlanState *planstate, const char *qlabel, |
| 62 | + int nkeys, AttrNumber *keycols, |
| 63 | + Oid *sortOperators, Oid *collations, bool *nullsFirst, |
| 64 | + List *ancestors, ExplainState *es); |
| 65 | + |
54 | 66 | /*
|
55 | 67 | * We have one slot for each item in the heap array. We use SlotNumber
|
56 | 68 | * to store slot indexes. This doesn't actually provide any formal
|
@@ -472,6 +484,12 @@ runtimemergeappend_explain(CustomScanState *node, List *ancestors, ExplainState
|
472 | 484 | RuntimeMergeAppendState *scan_state = (RuntimeMergeAppendState *) node;
|
473 | 485 |
|
474 | 486 | explain_append_common(node, scan_state->rstate.children_table, es);
|
| 487 | + |
| 488 | + /* We should print sort keys as well */ |
| 489 | + show_sort_group_keys((PlanState *) &node->ss.ps, "Sort Key", |
| 490 | + scan_state->numCols, scan_state->sortColIdx, |
| 491 | + scan_state->sortOperators, scan_state->collations, |
| 492 | + scan_state->nullsFirst, ancestors, es); |
475 | 493 | }
|
476 | 494 |
|
477 | 495 |
|
@@ -794,3 +812,118 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
|
794 | 812 |
|
795 | 813 | return lefttree;
|
796 | 814 | }
|
| 815 | + |
| 816 | +/* |
| 817 | + * Append nondefault characteristics of the sort ordering of a column to buf |
| 818 | + * (collation, direction, NULLS FIRST/LAST) |
| 819 | + */ |
| 820 | +static void |
| 821 | +show_sortorder_options(StringInfo buf, Node *sortexpr, |
| 822 | + Oid sortOperator, Oid collation, bool nullsFirst) |
| 823 | +{ |
| 824 | + Oid sortcoltype = exprType(sortexpr); |
| 825 | + bool reverse = false; |
| 826 | + TypeCacheEntry *typentry; |
| 827 | + |
| 828 | + typentry = lookup_type_cache(sortcoltype, |
| 829 | + TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); |
| 830 | + |
| 831 | + /* |
| 832 | + * Print COLLATE if it's not default. There are some cases where this is |
| 833 | + * redundant, eg if expression is a column whose declared collation is |
| 834 | + * that collation, but it's hard to distinguish that here. |
| 835 | + */ |
| 836 | + if (OidIsValid(collation) && collation != DEFAULT_COLLATION_OID) |
| 837 | + { |
| 838 | + char *collname = get_collation_name(collation); |
| 839 | + |
| 840 | + if (collname == NULL) |
| 841 | + elog(ERROR, "cache lookup failed for collation %u", collation); |
| 842 | + appendStringInfo(buf, " COLLATE %s", quote_identifier(collname)); |
| 843 | + } |
| 844 | + |
| 845 | + /* Print direction if not ASC, or USING if non-default sort operator */ |
| 846 | + if (sortOperator == typentry->gt_opr) |
| 847 | + { |
| 848 | + appendStringInfoString(buf, " DESC"); |
| 849 | + reverse = true; |
| 850 | + } |
| 851 | + else if (sortOperator != typentry->lt_opr) |
| 852 | + { |
| 853 | + char *opname = get_opname(sortOperator); |
| 854 | + |
| 855 | + if (opname == NULL) |
| 856 | + elog(ERROR, "cache lookup failed for operator %u", sortOperator); |
| 857 | + appendStringInfo(buf, " USING %s", opname); |
| 858 | + /* Determine whether operator would be considered ASC or DESC */ |
| 859 | + (void) get_equality_op_for_ordering_op(sortOperator, &reverse); |
| 860 | + } |
| 861 | + |
| 862 | + /* Add NULLS FIRST/LAST only if it wouldn't be default */ |
| 863 | + if (nullsFirst && !reverse) |
| 864 | + { |
| 865 | + appendStringInfoString(buf, " NULLS FIRST"); |
| 866 | + } |
| 867 | + else if (!nullsFirst && reverse) |
| 868 | + { |
| 869 | + appendStringInfoString(buf, " NULLS LAST"); |
| 870 | + } |
| 871 | +} |
| 872 | + |
| 873 | +/* |
| 874 | + * Common code to show sort/group keys, which are represented in plan nodes |
| 875 | + * as arrays of targetlist indexes. If it's a sort key rather than a group |
| 876 | + * key, also pass sort operators/collations/nullsFirst arrays. |
| 877 | + */ |
| 878 | +static void |
| 879 | +show_sort_group_keys(PlanState *planstate, const char *qlabel, |
| 880 | + int nkeys, AttrNumber *keycols, |
| 881 | + Oid *sortOperators, Oid *collations, bool *nullsFirst, |
| 882 | + List *ancestors, ExplainState *es) |
| 883 | +{ |
| 884 | + Plan *plan = planstate->plan; |
| 885 | + List *context; |
| 886 | + List *result = NIL; |
| 887 | + StringInfoData sortkeybuf; |
| 888 | + bool useprefix; |
| 889 | + int keyno; |
| 890 | + |
| 891 | + if (nkeys <= 0) |
| 892 | + return; |
| 893 | + |
| 894 | + initStringInfo(&sortkeybuf); |
| 895 | + |
| 896 | + /* Set up deparsing context */ |
| 897 | + context = set_deparse_context_planstate(es->deparse_cxt, |
| 898 | + (Node *) planstate, |
| 899 | + ancestors); |
| 900 | + useprefix = (list_length(es->rtable) > 1 || es->verbose); |
| 901 | + |
| 902 | + for (keyno = 0; keyno < nkeys; keyno++) |
| 903 | + { |
| 904 | + /* find key expression in tlist */ |
| 905 | + AttrNumber keyresno = keycols[keyno]; |
| 906 | + TargetEntry *target = get_tle_by_resno(plan->targetlist, |
| 907 | + keyresno); |
| 908 | + char *exprstr; |
| 909 | + |
| 910 | + if (!target) |
| 911 | + elog(ERROR, "no tlist entry for key %d", keyresno); |
| 912 | + /* Deparse the expression, showing any top-level cast */ |
| 913 | + exprstr = deparse_expression((Node *) target->expr, context, |
| 914 | + useprefix, true); |
| 915 | + resetStringInfo(&sortkeybuf); |
| 916 | + appendStringInfoString(&sortkeybuf, exprstr); |
| 917 | + /* Append sort order information, if relevant */ |
| 918 | + if (sortOperators != NULL) |
| 919 | + show_sortorder_options(&sortkeybuf, |
| 920 | + (Node *) target->expr, |
| 921 | + sortOperators[keyno], |
| 922 | + collations[keyno], |
| 923 | + nullsFirst[keyno]); |
| 924 | + /* Emit one property-list item per sort key */ |
| 925 | + result = lappend(result, pstrdup(sortkeybuf.data)); |
| 926 | + } |
| 927 | + |
| 928 | + ExplainPropertyList(qlabel, result, es); |
| 929 | +} |
0 commit comments