From 1e9b02e0f8cb307e824ef8ba56d5a9986fdcc023 Mon Sep 17 00:00:00 2001 From: mtimm_01 Date: Wed, 7 Jan 2015 15:32:39 +0100 Subject: [PATCH] explain_sortorder added --- src/backend/commands/explain.c | 107 +++++++++++++++++++++--- src/test/regress/expected/aggregates.out | 2 +- src/test/regress/expected/equivclass.out | 4 +- src/test/regress/expected/explain_sortorder.out | 16 ++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/explain_sortorder.sql | 12 +++ 7 files changed, 130 insertions(+), 14 deletions(-) create mode 100644 src/test/regress/expected/explain_sortorder.out create mode 100644 src/test/regress/sql/explain_sortorder.sql diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 8a0be5d..ba4d17a 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -3,7 +3,7 @@ * explain.c * Explain query execution plans * - * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994-5, Regents of the University of California * * IDENTIFICATION @@ -14,6 +14,9 @@ #include "postgres.h" #include "access/xact.h" +#include "access/htup_details.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_operator.h" #include "catalog/pg_type.h" #include "commands/createas.h" #include "commands/defrem.h" @@ -30,8 +33,11 @@ #include "utils/rel.h" #include "utils/ruleutils.h" #include "utils/snapmgr.h" +#include "utils/syscache.h" #include "utils/tuplesort.h" +#include "utils/typcache.h" #include "utils/xml.h" +#include "nodes/nodeFuncs.h" /* Hook for plugins to get control in ExplainOneQuery() */ @@ -84,6 +90,7 @@ static void show_group_keys(GroupState *gstate, List *ancestors, static void show_sort_group_keys(PlanState *planstate, const char *qlabel, int nkeys, AttrNumber *keycols, List *ancestors, ExplainState *es); +static char *get_sortorder_by_keyno(SortState *sortstate, ExplainState *es, int keyno); 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, @@ -116,7 +123,6 @@ static void ExplainYAMLLineStarting(ExplainState *es); static void escape_yaml(StringInfo buf, const char *str); - /* * ExplainQuery - * execute an EXPLAIN command @@ -1435,6 +1441,8 @@ ExplainNode(PlanState *planstate, List *ancestors, break; case T_Sort: show_sort_keys((SortState *) planstate, ancestors, es); + //if (es->verbose) + // show_sort_order((SortState *)planstate, es); show_sort_info((SortState *) planstate, es); break; case T_MergeAppend: @@ -1863,23 +1871,102 @@ show_sort_group_keys(PlanState *planstate, const char *qlabel, for (keyno = 0; keyno < nkeys; keyno++) { - /* find key expression in tlist */ - AttrNumber keyresno = keycols[keyno]; - TargetEntry *target = get_tle_by_resno(plan->targetlist, - keyresno); - + /* find key ekeynoxpression in tlist */ + AttrNumber keyresno; + TargetEntry *target; + char *sortorder; + int maxlength; + char *element_concat_sortorder; + + keyresno = keycols[keyno]; + target = get_tle_by_resno(plan->targetlist, keyresno); + if (nodeTag(plan) == T_Sort) { + sortorder = get_sortorder_by_keyno((SortState *) planstate, es, keyno); + } else { + sortorder = ""; + } 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); + exprstr = deparse_expression((Node *) target->expr, context, useprefix, true); + maxlength = strlen(exprstr)+1+strlen(sortorder)+1; + element_concat_sortorder = (char *) malloc((maxlength + 1) * sizeof(char)); + snprintf(element_concat_sortorder, maxlength , "%s%s", exprstr, sortorder); + result = lappend(result, element_concat_sortorder); } ExplainPropertyList(qlabel, result, es); } /* + * In verbose mode, additional information about the collation, sort order + * and NULLS FIRST/LAST is printed + */ +static char * +get_sortorder_by_keyno(SortState *sortstate, ExplainState *es, int keyno) +{ + Sort *plan; + Plan *planstatePlan; + Oid sortcoltype; + TypeCacheEntry *typentry; + HeapTuple opertup; + Form_pg_operator operform; + char *oprname; + int maxItemLength; + char *sort_order_values; + Oid collId; + Oid operId; + AttrNumber keyresno; + TargetEntry *target; + + plan = (Sort *) sortstate->ss.ps.plan; + planstatePlan = (Plan *) sortstate->ss.ps.plan; + maxItemLength = 200; + collId = plan->collations[keyno]; + operId = plan->sortOperators[keyno]; + keyresno = plan->sortColIdx[keyno]; + target = get_tle_by_resno(planstatePlan->targetlist, keyresno); + sortcoltype = exprType((const Node *) target->expr); + typentry = lookup_type_cache(sortcoltype, TYPECACHE_LT_OPR | TYPECACHE_GT_OPR); + sort_order_values = (char *) malloc((maxItemLength + 1) * sizeof(char)); + strcpy(sort_order_values, ""); + + if (OidIsValid(collId) && collId != DEFAULT_COLLATION_OID) + { + char *collname; + char *localeptr; + + collname = get_collation_name(collId); + localeptr = setlocale(LC_COLLATE, NULL); + + snprintf(sort_order_values + strlen(sort_order_values), maxItemLength - strlen(sort_order_values), " COLLATE '%s'", collname); + /* for those who use COLLATE although their default is already + * the wanted */ + if (strcmp(collname, localeptr) == 0) + { + snprintf(sort_order_values + strlen(sort_order_values), maxItemLength - strlen(sort_order_values), " (%s is LC_COLLATE)", collname); + } + } + if (operId == typentry->gt_opr) + { + snprintf(sort_order_values + strlen(sort_order_values), maxItemLength - strlen(sort_order_values), "%s", " DESC"); + } + else if (operId != typentry->lt_opr) + { + opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operId)); + operform = (Form_pg_operator) GETSTRUCT (opertup); + oprname = NameStr(operform->oprname); + snprintf(sort_order_values + strlen(sort_order_values), maxItemLength - strlen(sort_order_values), " USING %s", oprname); + ReleaseSysCache(opertup); + } + if (plan->nullsFirst[keyno]) + { + snprintf(sort_order_values + strlen(sort_order_values), maxItemLength - strlen(sort_order_values), " NULLS FIRST"); + } + return sort_order_values; +} + +/* * 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 40f5398..ca8aec8 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 NULLS FIRST InitPlan 1 (returns $0) -> Limit -> Index Only Scan Backward using tenk1_unique2 on tenk1 diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out index b312292..dfae84e 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/expected/explain_sortorder.out b/src/test/regress/expected/explain_sortorder.out new file mode 100644 index 0000000..abd8130 --- /dev/null +++ b/src/test/regress/expected/explain_sortorder.out @@ -0,0 +1,16 @@ +-- +-- Test explain feature: sort order +-- +CREATE TABLE sortordertest (n1 char(1), n2 int4); +-- Insert values by which should be ordered +INSERT INTO sortordertest(n1, n2) VALUES ('d', 5), ('b', 3), ('a', 1), ('e', 2), ('c', 4); +-- Display sort order when explain analyze and verbose are true. +EXPLAIN (COSTS OFF) SELECT * FROM sortordertest ORDER BY n1 COLLATE "C" DESC, n2; + QUERY PLAN +------------------------------------------------- + Sort + Sort Key: n1 COLLATE 'C' DESC NULLS FIRST, n2 + -> Seq Scan on sortordertest +(3 rows) + +DROP TABLE sortordertest; diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 62ef6ec..3cf63ff 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -59,7 +59,7 @@ test: create_index create_view # ---------- # Another group of parallel tests # ---------- -test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views +test: create_aggregate create_function_3 create_cast constraints triggers inherit create_table_like typed_table vacuum drop_if_exists updatable_views explain_sortorder # ---------- # sanity_check does a vacuum, affecting the sort order of SELECT * diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index b491b97..dc242f1 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -71,6 +71,7 @@ test: typed_table test: vacuum test: drop_if_exists test: updatable_views +test: explain_sortorder test: sanity_check test: errors test: select diff --git a/src/test/regress/sql/explain_sortorder.sql b/src/test/regress/sql/explain_sortorder.sql new file mode 100644 index 0000000..d1800a3 --- /dev/null +++ b/src/test/regress/sql/explain_sortorder.sql @@ -0,0 +1,12 @@ +-- +-- Test explain feature: sort order +-- +CREATE TABLE sortordertest (n1 char(1), n2 int4); + +-- Insert values by which should be ordered +INSERT INTO sortordertest(n1, n2) VALUES ('d', 5), ('b', 3), ('a', 1), ('e', 2), ('c', 4); + +-- Display sort order when explain analyze and verbose are true. +EXPLAIN (COSTS OFF) SELECT * FROM sortordertest ORDER BY n1 COLLATE "C" DESC, n2; + +DROP TABLE sortordertest; \ No newline at end of file -- 1.9.1