OSDN Git Service

Support ORDER BY and GROUPING SETS in text representation.
authorKyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Fri, 26 Aug 2016 04:16:47 +0000 (13:16 +0900)
committerKyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Fri, 26 Aug 2016 07:10:09 +0000 (16:10 +0900)
Text plan representaion omitted these items. This patch make them
properly shown.
Addition to that, GroupKeys (ON CONFLICT) introduced another way of
indentation of brackets. Make the bracket indentaion code behave
differently for the case.

json2sql.pl
makeplanfile.sql
pgsp_json.c
pgsp_json_int.h
pgsp_json_text.c
pgsp_json_text.h

index aa5c146..b87ff36 100755 (executable)
@@ -218,6 +218,9 @@ sub setplan0 {
   "Sort Method": "external merge",
   "Sort Method": "still in progress",
   "Sort Key": "a",
+  "Group Key": "a",
+  "Grouping Sets": "a",
+  "Group Keys": "a",
   "Filter": "a",
   "Join Filter": "a",
   "Hash Cond": "a",
index d40b5a6..1e54ab2 100644 (file)
@@ -172,5 +172,11 @@ explain (analyze on, buffers on, verbose on, format :format)
 \echo ###### ON CONFLICT
 explain (analyze on, buffers on, verbose on, format :format)
    INSERT INTO ct1 VALUES (1,1) ON CONFLICT (a) DO UPDATE SET b = EXCLUDED.b + 1;
+\echo ###### GROUP BY
+explain (analyze on, buffers on, verbose on, format :format)
+   SELECT a, b, max(c) FROM tt1 GROUP BY a, b;
+\echo ###### GROUPING SETS
+explain (analyze on, buffers on, verbose on, format :format)
+   SELECT a, b, max(c) FROM tt1 GROUP BY GROUPING SETS ((a), (b), ());
 
 -- BitmapAnd/Inner/Right/ForegnScan
index 5a65939..a7dc881 100644 (file)
@@ -94,6 +94,9 @@ word_table propfields[] =
        {P_ConstraintName,      "x" ,"Constraint Name",         NULL, true,  NULL,                              NULL},
        {P_Plans,                       "l" ,"Plans",                           NULL, true,  NULL,                              NULL},
        {P_Plan,                        "p" ,"Plan",                            NULL, true,  NULL,                              NULL},
+       {P_GroupKey,            "-" ,"Group Key",                       NULL, true,  NULL,                              SETTER(group_key)},
+       {P_GroupSets,           "=" ,"Grouping Sets",           NULL, true,  NULL,                              NULL},
+       {P_GroupKeys,           "\\" ,"Group Keys",                     NULL, true,  NULL,                              SETTER(group_key)},
                                                                                                                  
        /* Values of these properties are ignored on normalization */
        {P_FunctionCall,        "y" ,"Function Call",           NULL, false, NULL,                              SETTER(func_call)},
@@ -635,6 +638,9 @@ json_arrstart(void *state)
 {
        pgspParserContext *ctx = (pgspParserContext *)state;
 
+       if (ctx->current_list == P_GroupKeys)
+               ctx->wlist_level++;
+
        appendStringInfoChar(ctx->dest, '[');
        ctx->fname = NULL;
        ctx->level++;
@@ -646,8 +652,12 @@ static void
 json_arrend(void *state)
 {
        pgspParserContext *ctx = (pgspParserContext *)state;
-       if (ctx->mode == PGSP_JSON_INFLATE &&
-               ctx->last_elem_is_object)
+
+       if (ctx->current_list == P_GroupKeys)
+               ctx->wlist_level--;
+
+       if (ctx->current_list == P_GroupKeys ? ctx->wlist_level == 0 :
+               (ctx->mode == PGSP_JSON_INFLATE && ctx->last_elem_is_object))
        {
                appendStringInfoChar(ctx->dest, '\n');
                appendStringInfoSpaces(ctx->dest, (ctx->level - 1) * INDENT_STEP);
@@ -706,6 +716,25 @@ json_ofstart(void *state, char *fname, bool isnull)
 
        if (ctx->mode == PGSP_JSON_INFLATE)
                appendStringInfoChar(ctx->dest, ' ');
+
+       if (p && p->tag == P_GroupKeys)
+       {
+               ctx->current_list = p->tag;
+               ctx->list_fname = fname;
+               ctx->wlist_level = 0;
+       }
+}
+
+static void
+json_ofend(void *state, char *fname, bool isnull)
+{
+       pgspParserContext *ctx = (pgspParserContext *)state;
+
+       if (ctx->list_fname && strcmp(fname, ctx->list_fname) == 0)
+       {
+               ctx->list_fname = NULL;
+               ctx->current_list = P_Invalid;
+       }
 }
 
 static void
@@ -715,15 +744,28 @@ json_aestart(void *state, bool isnull)
        if (ctx->remove)
                return;
 
-       if (!bms_is_member(ctx->level, ctx->first))
+       if (ctx->current_list == P_GroupKeys &&
+               ctx->wlist_level == 1)
        {
-               appendStringInfoChar(ctx->dest, ',');
-               if (ctx->mode == PGSP_JSON_INFLATE &&
-                       !ctx->last_elem_is_object)
-                       appendStringInfoChar(ctx->dest, ' ');
+               if (!bms_is_member(ctx->level, ctx->first))
+                       appendStringInfoChar(ctx->dest, ',');
+               
+               appendStringInfoChar(ctx->dest, '\n');
+               appendStringInfoSpaces(ctx->dest, (ctx->level) * INDENT_STEP);
        }
        else
-               ctx->first = bms_del_member(ctx->first, ctx->level);
+       {
+               if (!bms_is_member(ctx->level, ctx->first))
+               {
+                       appendStringInfoChar(ctx->dest, ',');
+
+                       if (ctx->mode == PGSP_JSON_INFLATE &&
+                               !ctx->last_elem_is_object)
+                               appendStringInfoChar(ctx->dest, ' ');
+               }
+       }
+
+       ctx->first = bms_del_member(ctx->first, ctx->level);
 }
 
 static void
@@ -1125,7 +1167,7 @@ init_json_semaction(JsonSemAction *sem, pgspParserContext *ctx)
        sem->array_start        = json_arrstart;
        sem->array_end          = json_arrend;
        sem->object_field_start = json_ofstart;
-       sem->object_field_end   = NULL;
+       sem->object_field_end   = json_ofend;
        sem->array_element_start= json_aestart;
        sem->array_element_end  = NULL;
        sem->scalar             = json_scalar;
index 93a5a67..3d9fdd8 100644 (file)
@@ -52,6 +52,9 @@ typedef enum
        P_Command,
        P_SortMethod,
        P_SortKey,
+       P_GroupKey,
+       P_GroupKeys,
+       P_GroupSets,
        P_Filter,
        P_JoinFilter,
        P_HashCond,
@@ -141,7 +144,7 @@ typedef struct
        bool            remove;                 /* True if the current node is not shown in
                                                                 * the result */
        bool            last_elem_is_object; /* True if the last processed element
-                                                                * was not an object */
+                                                                * was an object */
        pgsp_prop_tags  section;        /* explain section under processing */
        pgsp_prop_tags  current_list; /* current list tag that needs special treat*/
        StringInfo work_str;            /* StringInfor for very-short term usage */
@@ -149,6 +152,9 @@ typedef struct
        char       *fname;                      /* Field name*/
        char       *wbuf;                       /* Working buffer */
        int                     wbuflen;                /* Length of the working buffer */
+       int                     wlist_level;    /* Nest level of list for Grouping Sets */
+       grouping_set *tmp_gset; /* Working area for grouping sets */
+
        converter_t *valconverter;      /* field name converter for the current
                                                                 * element */
        setter_t    *setter;            /* value converter for the current element */
index 6455955..94a8bef 100644 (file)
@@ -32,6 +32,8 @@ static void print_prop_if_nz(StringInfo s, char *prepstr,
                                                         const char *prop, int leve, int exind);
 static void json_text_objstart(void *state);
 static void json_text_objend(void *state);
+static void json_text_arrstart(void *state);
+static void json_text_arrend(void *state);
 static void json_text_ofstart(void *state, char *fname, bool isnull);
 static void json_text_ofend(void *state, char *fname, bool isnull);
 static void json_text_scalar(void *state, char *token, JsonTokenType tokentype);
@@ -91,6 +93,7 @@ CONVERSION_SETTER(join_type, conv_jointype);
 CONVERSION_SETTER(setopcommand, conv_setsetopcommand);
 CONVERSION_SETTER(sort_method, conv_sortmethod);
 LIST_SETTER(sort_key);
+LIST_SETTER(group_key);
 SQLQUOTE_SETTER(index_name);
 DEFAULT_SETTER(startup_cost);
 DEFAULT_SETTER(total_cost);
@@ -227,6 +230,30 @@ print_propstr_if_exists(StringInfo s, char *prepstr,
 }
 
 static void
+print_groupingsets_if_exists(StringInfo s, List *gss, int level, int exind)
+{
+       ListCell *lc;
+
+       foreach (lc, gss)
+       {
+               ListCell *lcg;
+               grouping_set *gs = (grouping_set *)lfirst (lc);
+
+               if (gs->sort_keys)
+               {
+                       print_prop_if_exists(s, "Sort Key: ", gs->sort_keys, level, exind);
+                       exind += 2;
+               }
+
+               foreach (lcg, gs->group_keys)
+               {
+                       const char *gk = (const char *)lfirst (lcg);
+                       print_prop_if_exists(s, "Group Key: ", gk, level, exind);
+               }
+       }
+}
+
+static void
 print_prop_if_nz(StringInfo s, char *prepstr,
                                 const char *prop, int level, int exind)
 {
@@ -368,6 +395,8 @@ print_current_node(pgspParserContext *ctx)
        }
                
        print_propstr_if_exists(s, "Output: ", v->output, level, exind);
+       print_propstr_if_exists(s, "Group Key: ", v->group_key, level, exind);
+       print_groupingsets_if_exists(s, v->grouping_sets, level, exind);
        print_prop_if_exists(s, "Merge Cond: ", v->merge_cond, level, exind);
        print_prop_if_exists(s, "Hash Cond: " , v->hash_cond, level, exind);
        print_prop_if_exists(s, "Tid Cond: " , v->tid_cond, level, exind);
@@ -617,6 +646,20 @@ json_text_objstart(void *state)
 {
        pgspParserContext *ctx = (pgspParserContext *)state;
        ctx->level++;
+
+       /* Create new grouping sets or reset existing ones */
+       if (ctx->current_list == P_GroupSets)
+       {
+               node_vals *v = ctx->nodevals;
+
+               ctx->tmp_gset = (grouping_set*) palloc0(sizeof(grouping_set));
+               if (!v->sort_key)
+                       v->sort_key = makeStringInfo();
+               if (!v->group_key)
+                       v->group_key = makeStringInfo();
+               resetStringInfo(v->sort_key);
+               resetStringInfo(v->group_key);
+       }
 }
 
 static void
@@ -650,11 +693,70 @@ json_text_objend(void *state)
                                                                   pstrdup(ctx->work_str->data));
                resetStringInfo(ctx->work_str);
        }
+       else if (ctx->current_list == P_GroupSets && ctx->tmp_gset)
+       {
+               /* Move working grouping set into nodevals */
+               node_vals *v = ctx->nodevals;
+
+               /* Copy sort key if any */
+               if (v->sort_key->data[0])
+               {
+                       ctx->tmp_gset->sort_keys = strdup(v->sort_key->data);
+                       resetStringInfo(v->sort_key);
+               }
+
+               /* Move working grouping set into nodevals */
+               ctx->nodevals->grouping_sets = 
+                       lappend(v->grouping_sets, ctx->tmp_gset);
+               ctx->tmp_gset = NULL;
+       }
 
        ctx->last_elem_is_object = true;
        ctx->level--;
 }
 
+static void 
+json_text_arrstart(void *state)
+{
+       pgspParserContext *ctx = (pgspParserContext *)state;
+
+       if (ctx->current_list == P_GroupSets)
+       {
+               ctx->wlist_level++;
+       }
+}
+
+static void
+json_text_arrend(void *state)
+{
+       pgspParserContext *ctx = (pgspParserContext *)state;
+
+       if (ctx->current_list == P_GroupSets)
+       {
+               /*
+                * wlist_level means that now at the end of innermost list of Group
+                * Keys
+                */
+               if (ctx->wlist_level  == 3)
+               {
+                       node_vals *v = ctx->nodevals;
+
+                       /*
+                        * At this point, v->group_key holds the keys in "Group Keys". The
+                        * item holds a double-nested list and the innermost lists are to
+                        * go into individual "Group Key" lines. Empty innermost list is
+                        * represented as "()" there. See explain.c of PostgreSQL.
+                        */
+                       ctx->tmp_gset->group_keys =
+                               lappend(ctx->tmp_gset->group_keys,
+                                               (v->group_key->data[0] ?
+                                                pstrdup(v->group_key->data) : "()"));
+                       resetStringInfo(ctx->nodevals->group_key);
+               }
+               ctx->wlist_level--;
+       }
+}
+
 static void
 json_text_ofstart(void *state, char *fname, bool isnull)
 {
@@ -693,6 +795,12 @@ json_text_ofstart(void *state, char *fname, bool isnull)
                        v->tmp_schema_name = v->schema_name;
                        v->tmp_alias = v->alias;
                }
+               else if (p->tag == P_GroupSets)
+               {
+                       ctx->current_list = p->tag;
+                       ctx->list_fname = fname;
+                       ctx->wlist_level = 0;
+               }
 
                /*
                 * This paser prints partial result at the end of every P_Plan object,
@@ -725,6 +833,7 @@ json_text_ofend(void *state, char *fname, bool isnull)
                        v->schema_name = v->tmp_schema_name;
                        v->alias = v->tmp_alias;
                }
+
                ctx->list_fname = NULL;
                ctx->current_list = P_Invalid;
        }
@@ -773,8 +882,8 @@ pgsp_json_textize(char *json)
        sem.semstate = (void*)&ctx;
        sem.object_start       = json_text_objstart;
        sem.object_end         = json_text_objend;
-       sem.array_start        = NULL;
-       sem.array_end          = NULL;
+       sem.array_start        = json_text_arrstart;
+       sem.array_end          = json_text_arrend;
        sem.object_field_start = json_text_ofstart;
        sem.object_field_end   = json_text_ofend;
        sem.array_element_start= NULL;
index 8886a13..3e3790a 100644 (file)
 
 typedef struct
 {
+       const char *sort_keys;
+       List *group_keys;
+} grouping_set;
+
+typedef struct
+{
        NodeTag nodetag;
        const char *node_type;
        const char *operation;
@@ -31,6 +37,8 @@ typedef struct
        const char *func_call;
        const char *sort_method;
        StringInfo sort_key;
+       StringInfo group_key;
+       List       *grouping_sets;
        const char *index_cond;
        const char *merge_cond;
        const char *hash_cond;
@@ -98,7 +106,7 @@ typedef struct
 
 #define LIST_SETTER(name) \
        SETTERDECL(name) { \
-               if (!vals->name)\
+               if (!vals->name || !vals->name->data[0])\
                { \
                        vals->name = makeStringInfo(); \
                        appendStringInfoString(vals->name, val); \
@@ -127,6 +135,8 @@ SETTERDECL(join_type);
 SETTERDECL(setopcommand);
 SETTERDECL(sort_method);
 SETTERDECL(sort_key);
+SETTERDECL(group_key);
+SETTERDECL(group_keys);
 SETTERDECL(index_name);
 SETTERDECL(startup_cost);
 SETTERDECL(total_cost);