aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/explain.c90
-rw-r--r--src/test/regress/expected/aggregates.out6
-rw-r--r--src/test/regress/expected/collate.out19
-rw-r--r--src/test/regress/expected/equivclass.out4
-rw-r--r--src/test/regress/sql/collate.sql9
5 files changed, 120 insertions, 8 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 2b84ac8af37..7cfc9bb612a 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -14,12 +14,14 @@
#include "postgres.h"
#include "access/xact.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "commands/createas.h"
#include "commands/defrem.h"
#include "commands/prepare.h"
#include "executor/hashjoin.h"
#include "foreign/fdwapi.h"
+#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteHandler.h"
@@ -31,6 +33,7 @@
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/tuplesort.h"
+#include "utils/typcache.h"
#include "utils/xml.h"
@@ -83,7 +86,10 @@ static void show_group_keys(GroupState *gstate, List *ancestors,
ExplainState *es);
static void show_sort_group_keys(PlanState *planstate, const char *qlabel,
int nkeys, AttrNumber *keycols,
+ Oid *sortOperators, Oid *collations, bool *nullsFirst,
List *ancestors, ExplainState *es);
+static void show_sortorder_options(StringInfo buf, Node *sortexpr,
+ Oid sortOperator, Oid collation, bool nullsFirst);
static void show_sort_info(SortState *sortstate, ExplainState *es);
static void show_hash_info(HashState *hashstate, ExplainState *es);
static void show_tidbitmap_info(BitmapHeapScanState *planstate,
@@ -1781,6 +1787,8 @@ show_sort_keys(SortState *sortstate, List *ancestors, ExplainState *es)
show_sort_group_keys((PlanState *) sortstate, "Sort Key",
plan->numCols, plan->sortColIdx,
+ plan->sortOperators, plan->collations,
+ plan->nullsFirst,
ancestors, es);
}
@@ -1795,6 +1803,8 @@ show_merge_append_keys(MergeAppendState *mstate, List *ancestors,
show_sort_group_keys((PlanState *) mstate, "Sort Key",
plan->numCols, plan->sortColIdx,
+ plan->sortOperators, plan->collations,
+ plan->nullsFirst,
ancestors, es);
}
@@ -1813,6 +1823,7 @@ show_agg_keys(AggState *astate, List *ancestors,
ancestors = lcons(astate, ancestors);
show_sort_group_keys(outerPlanState(astate), "Group Key",
plan->numCols, plan->grpColIdx,
+ NULL, NULL, NULL,
ancestors, es);
ancestors = list_delete_first(ancestors);
}
@@ -1831,29 +1842,34 @@ show_group_keys(GroupState *gstate, List *ancestors,
ancestors = lcons(gstate, ancestors);
show_sort_group_keys(outerPlanState(gstate), "Group Key",
plan->numCols, plan->grpColIdx,
+ NULL, NULL, NULL,
ancestors, es);
ancestors = list_delete_first(ancestors);
}
/*
* Common code to show sort/group keys, which are represented in plan nodes
- * as arrays of targetlist indexes
+ * as arrays of targetlist indexes. If it's a sort key rather than a group
+ * key, also pass sort operators/collations/nullsFirst arrays.
*/
static void
show_sort_group_keys(PlanState *planstate, const char *qlabel,
int nkeys, AttrNumber *keycols,
+ Oid *sortOperators, Oid *collations, bool *nullsFirst,
List *ancestors, ExplainState *es)
{
Plan *plan = planstate->plan;
List *context;
List *result = NIL;
+ StringInfoData sortkeybuf;
bool useprefix;
int keyno;
- char *exprstr;
if (nkeys <= 0)
return;
+ initStringInfo(&sortkeybuf);
+
/* Set up deparsing context */
context = set_deparse_context_planstate(es->deparse_cxt,
(Node *) planstate,
@@ -1866,19 +1882,87 @@ show_sort_group_keys(PlanState *planstate, const char *qlabel,
AttrNumber keyresno = keycols[keyno];
TargetEntry *target = get_tle_by_resno(plan->targetlist,
keyresno);
+ char *exprstr;
if (!target)
elog(ERROR, "no tlist entry for key %d", keyresno);
/* Deparse the expression, showing any top-level cast */
exprstr = deparse_expression((Node *) target->expr, context,
useprefix, true);
- result = lappend(result, exprstr);
+ resetStringInfo(&sortkeybuf);
+ appendStringInfoString(&sortkeybuf, exprstr);
+ /* Append sort order information, if relevant */
+ if (sortOperators != NULL)
+ show_sortorder_options(&sortkeybuf,
+ (Node *) target->expr,
+ sortOperators[keyno],
+ collations[keyno],
+ nullsFirst[keyno]);
+ /* Emit one property-list item per sort key */
+ result = lappend(result, pstrdup(sortkeybuf.data));
}
ExplainPropertyList(qlabel, result, es);
}
/*
+ * Append nondefault characteristics of the sort ordering of a column to buf
+ * (collation, direction, NULLS FIRST/LAST)
+ */
+static void
+show_sortorder_options(StringInfo buf, Node *sortexpr,
+ Oid sortOperator, Oid collation, bool nullsFirst)
+{
+ Oid sortcoltype = exprType(sortexpr);
+ bool reverse = false;
+ TypeCacheEntry *typentry;
+
+ typentry = lookup_type_cache(sortcoltype,
+ TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
+
+ /*
+ * Print COLLATE if it's not default. There are some cases where this is
+ * redundant, eg if expression is a column whose declared collation is
+ * that collation, but it's hard to distinguish that here.
+ */
+ if (OidIsValid(collation) && collation != DEFAULT_COLLATION_OID)
+ {
+ char *collname = get_collation_name(collation);
+
+ if (collname == NULL)
+ elog(ERROR, "cache lookup failed for collation %u", collation);
+ appendStringInfo(buf, " COLLATE %s", quote_identifier(collname));
+ }
+
+ /* Print direction if not ASC, or USING if non-default sort operator */
+ if (sortOperator == typentry->gt_opr)
+ {
+ appendStringInfoString(buf, " DESC");
+ reverse = true;
+ }
+ else if (sortOperator != typentry->lt_opr)
+ {
+ char *opname = get_opname(sortOperator);
+
+ if (opname == NULL)
+ elog(ERROR, "cache lookup failed for operator %u", sortOperator);
+ appendStringInfo(buf, " USING %s", opname);
+ /* Determine whether operator would be considered ASC or DESC */
+ (void) get_equality_op_for_ordering_op(sortOperator, &reverse);
+ }
+
+ /* Add NULLS FIRST/LAST only if it wouldn't be default */
+ if (nullsFirst && !reverse)
+ {
+ appendStringInfoString(buf, " NULLS FIRST");
+ }
+ else if (!nullsFirst && reverse)
+ {
+ appendStringInfoString(buf, " NULLS LAST");
+ }
+}
+
+/*
* If it's EXPLAIN ANALYZE, show tuplesort stats for a sort node
*/
static void
diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out
index 40f5398b72c..8852051e932 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -735,7 +735,7 @@ explain (costs off)
QUERY PLAN
---------------------------------------------------------------------
Sort
- Sort Key: (generate_series(1, 3))
+ Sort Key: (generate_series(1, 3)) DESC
InitPlan 1 (returns $0)
-> Limit
-> Index Only Scan Backward using tenk1_unique2 on tenk1
@@ -784,7 +784,7 @@ explain (costs off)
InitPlan 2 (returns $1)
-> Limit
-> Merge Append
- Sort Key: minmaxtest_1.f1
+ Sort Key: minmaxtest_1.f1 DESC
-> Index Only Scan Backward using minmaxtesti on minmaxtest minmaxtest_1
Index Cond: (f1 IS NOT NULL)
-> Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest1_1
@@ -823,7 +823,7 @@ explain (costs off)
InitPlan 2 (returns $1)
-> Limit
-> Merge Append
- Sort Key: minmaxtest_1.f1
+ Sort Key: minmaxtest_1.f1 DESC
-> Index Only Scan Backward using minmaxtesti on minmaxtest minmaxtest_1
Index Cond: (f1 IS NOT NULL)
-> Index Only Scan Backward using minmaxtest1i on minmaxtest1 minmaxtest1_1
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
index 91d574dbe4c..f076a4dbae1 100644
--- a/src/test/regress/expected/collate.out
+++ b/src/test/regress/expected/collate.out
@@ -603,6 +603,25 @@ ALTER TABLE collate_test22 ADD FOREIGN KEY (f2) REFERENCES collate_test20;
RESET enable_seqscan;
RESET enable_hashjoin;
RESET enable_nestloop;
+-- EXPLAIN
+EXPLAIN (COSTS OFF)
+ SELECT * FROM collate_test10 ORDER BY x, y;
+ QUERY PLAN
+----------------------------------------------
+ Sort
+ Sort Key: x COLLATE "C", y COLLATE "POSIX"
+ -> Seq Scan on collate_test10
+(3 rows)
+
+EXPLAIN (COSTS OFF)
+ SELECT * FROM collate_test10 ORDER BY x DESC, y COLLATE "C" ASC NULLS FIRST;
+ QUERY PLAN
+-----------------------------------------------------------
+ Sort
+ Sort Key: x COLLATE "C" DESC, y COLLATE "C" NULLS FIRST
+ -> Seq Scan on collate_test10
+(3 rows)
+
-- 9.1 bug with useless COLLATE in an expression subject to length coercion
CREATE TEMP TABLE vctable (f1 varchar(25));
INSERT INTO vctable VALUES ('foo' COLLATE "C");
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index b312292c6f3..dfae84e1a5f 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -319,7 +319,7 @@ explain (costs off)
-> Index Scan using ec1_expr4 on ec1 ec1_3
-> Materialize
-> Sort
- Sort Key: ec1.f1
+ Sort Key: ec1.f1 USING <
-> Index Scan using ec1_pkey on ec1
Index Cond: (ff = 42::bigint)
(20 rows)
@@ -376,7 +376,7 @@ explain (costs off)
-> Index Scan using ec1_expr4 on ec1 ec1_3
-> Materialize
-> Sort
- Sort Key: ec1.f1
+ Sort Key: ec1.f1 USING <
-> Index Scan using ec1_pkey on ec1
Index Cond: (ff = 42::bigint)
(14 rows)
diff --git a/src/test/regress/sql/collate.sql b/src/test/regress/sql/collate.sql
index 63ab590f3a4..e8ca0856e34 100644
--- a/src/test/regress/sql/collate.sql
+++ b/src/test/regress/sql/collate.sql
@@ -220,6 +220,15 @@ RESET enable_seqscan;
RESET enable_hashjoin;
RESET enable_nestloop;
+
+-- EXPLAIN
+
+EXPLAIN (COSTS OFF)
+ SELECT * FROM collate_test10 ORDER BY x, y;
+EXPLAIN (COSTS OFF)
+ SELECT * FROM collate_test10 ORDER BY x DESC, y COLLATE "C" ASC NULLS FIRST;
+
+
-- 9.1 bug with useless COLLATE in an expression subject to length coercion
CREATE TEMP TABLE vctable (f1 varchar(25));