OSDN Git Service

Support multi-targetted ModifyTable in plan output in text-style
authorKyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Fri, 26 Aug 2016 05:02:51 +0000 (14:02 +0900)
committerKyotaro Horiguchi <horiguchi.kyotaro@lab.ntt.co.jp>
Fri, 26 Aug 2016 05:37:26 +0000 (14:37 +0900)
PostgreSQL 9.5 newly supports explicit target table listing for
ModifyTable and pg_store_plans doesn't show such plans sainly in text
style. This doesn't affect JSON, XML, YAML styles of output.

Note: JSON representation lacks information about whether every target
table is a Foreign relation or not so the result of
pg_store_plans_textplan tells as if all target tables are local
relations even if there's remote ones.

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

index d812d06..c5d99a7 100755 (executable)
@@ -224,6 +224,7 @@ sub setplan0 {
   "Index Cond": "a",
   "TID Cond": "a",
   "Recheck Cond": "a",
+  "Target Tables": "a",
   "Operation": "Insert",
   "Operation": "Delete",
   "Operation": "Update",
index 39f3291..4f8cbf8 100644 (file)
@@ -5,9 +5,11 @@ set work_mem = '1MB';
 drop table if exists tt1;
 drop table if exists tt2;
 drop table if exists tt3;
-create table tt1 (a int, b int not null, c text);
-create table tt2 (a int, b int, c text);
-create table tt3 (a int, b int, c text);
+drop table if exists p cascade;
+create table p (a int, b int, c text);
+create table tt1 (a int, b int not null, c text) inherits (p);
+create table tt2 (a int, b int, c text) inherits (p);
+create table tt3 (a int, b int, c text) inherits (p);
 create index i_tt1 on tt1(a);
 create index i_tt2 on tt2(a);
 create index i_tt3_a on tt3(a);
@@ -158,5 +160,11 @@ rollback;
 \echo ###### Materialize
 explain (analyze on, buffers on, verbose on, format :format)
    select * from tt1 where a = all(select b from tt2);
+\echo ###### Update on partitioned tables
+explain (analyze on, buffers on, verbose on, format :format)
+   UPDATE p SET b = b + 1;
+\echo ###### Delete on partitioned tables
+explain (analyze on, buffers on, verbose on, format :format)
+   DELETE FROM p WHERE a = 100;
 
 -- BitmapAnd/Inner/Right/ForegnScan
index c18981a..02e5a5d 100644 (file)
@@ -134,6 +134,7 @@ word_table propfields[] =
        {P_ExactHeapBlks,       "&" ,"Exact Heap Blocks",       NULL, false,  NULL,                             SETTER(exact_heap_blks)},
        {P_LossyHeapBlks,       "(" ,"Lossy Heap Blocks",       NULL, false,  NULL,                             SETTER(lossy_heap_blks)},
        {P_RowsJoinFltRemvd,")" ,"Rows Removed by Join Filter", NULL, false,  NULL,             SETTER(joinfilt_removed)},
+       {P_TargetTables,    "_" ,"Target Tables",               NULL, false,  NULL,                             NULL},
        {P_Invalid, NULL, NULL, NULL, false, NULL, NULL}
 };
 
index 16f6a4a..e92024e 100644 (file)
@@ -103,7 +103,8 @@ typedef enum
        P_ExecTime,
        P_ExactHeapBlks,
        P_LossyHeapBlks,
-       P_RowsJoinFltRemvd
+       P_RowsJoinFltRemvd,
+       P_TargetTables
 } pgsp_prop_tags;
 
 typedef struct
@@ -137,7 +138,10 @@ typedef struct
                                                                 * the result */
        bool            last_elem_is_object; /* True if the last processed element
                                                                 * was not an object */
-       pgsp_prop_tags  section;        /* Tag of the word under processing */
+       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 */
+       char       *list_fname;         /* the field name of the current_list */
        char       *fname;                      /* Field name*/
        char       *wbuf;                       /* Working buffer */
        int                     wbuflen;                /* Length of the working buffer */
index 0c47654..77a6585 100644 (file)
@@ -151,34 +151,41 @@ DEFAULT_SETTER(joinfilt_removed);
        (TEXT_INDENT_BASE(l, e) + ((l < 2) ? 2 : 6))
 
 static void
-print_obj_name(pgspParserContext *ctx)
+print_obj_name0(StringInfo s,
+                               const char *obj_name, const char *schema_name, const char *alias)
 {
-       node_vals *v = ctx->nodevals;
-       StringInfo s = ctx->dest;
        bool on_written = false;
 
-       if (HASSTRING(v->obj_name))
+       if (HASSTRING(obj_name))
        {
                on_written = true;
                appendStringInfoString(s, " on ");
-               if (HASSTRING(v->schema_name))
+               if (HASSTRING(schema_name))
                {
-                       appendStringInfoString(s, v->schema_name);
+                       appendStringInfoString(s, schema_name);
                        appendStringInfoChar(s, '.');
                }
-               appendStringInfoString(s, v->obj_name);
+               appendStringInfoString(s, obj_name);
        }
-       if (HASSTRING(v->alias) &&
-               (!HASSTRING(v->obj_name) || strcmp(v->obj_name, v->alias) != 0))
+       if (HASSTRING(alias) &&
+               (!HASSTRING(obj_name) || strcmp(obj_name, alias) != 0))
        {
                if (!on_written)
                        appendStringInfoString(s, " on ");
                else
                        appendStringInfoChar(s, ' ');
-               appendStringInfoString(s, v->alias);
+               appendStringInfoString(s, alias);
        }
 }
 
+static void
+print_obj_name(pgspParserContext *ctx)
+{
+       node_vals *v = ctx->nodevals;
+       StringInfo s = ctx->dest;
+
+       print_obj_name0(s, v->obj_name, v->schema_name, v->alias);
+}
 
 static void
 print_prop(StringInfo s, char *prepstr,
@@ -227,6 +234,7 @@ print_current_node(pgspParserContext *ctx)
 {
        node_vals *v = ctx->nodevals;
        StringInfo s = ctx->dest;
+       ListCell *lc;
        int level = ctx->level - 1;
        bool comma = false;
        int exind = 0;
@@ -245,8 +253,9 @@ print_current_node(pgspParserContext *ctx)
                exind = 2;
                appendStringInfoSpaces(s, TEXT_INDENT_BASE(level, exind));
        }
-       
-       if (level > 1)
+
+       /* list items doesn't need this header */
+       if (level > 1 && ctx->current_list == P_Invalid)
                appendStringInfoString(s, "->  ");
 
        switch (v->nodetag)
@@ -300,7 +309,11 @@ print_current_node(pgspParserContext *ctx)
                        appendStringInfoString(s, v->node_type);
                        break;
        }
-       
+
+       /* Don't show costs for child talbes */
+       if (ctx->current_list == P_TargetTables)
+               return;
+
        if (!ISZERO(v->startup_cost) &&
                !ISZERO(v->total_cost) &&
                HASSTRING(v->plan_rows) &&
@@ -340,6 +353,15 @@ print_current_node(pgspParserContext *ctx)
                appendStringInfoString(s, ")");
        }
 
+       foreach(lc, v->target_tables)
+       {
+               char *str = (char *)lfirst (lc);
+
+               appendStringInfoString(s, "\n");
+               appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
+               appendStringInfoString(s, str);
+       }
+               
        print_propstr_if_exists(s, "Output: ", v->output, 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);
@@ -600,6 +622,21 @@ json_text_objend(void *state)
                print_current_trig_node(ctx);
                clear_nodeval(ctx->nodevals);
        }
+       else if (ctx->current_list == P_TargetTables)
+       {
+               /* Move the current working taget tables into nodevals */
+               node_vals *v = ctx->nodevals;
+
+               if (!ctx->work_str)
+                       ctx->work_str = makeStringInfo();
+
+               resetStringInfo(ctx->work_str);
+               appendStringInfoString(ctx->work_str, v->operation);
+               print_obj_name0(ctx->work_str, v->obj_name, v->schema_name, v->alias);
+               v->target_tables = lappend(v->target_tables,
+                                                                  pstrdup(ctx->work_str->data));
+               resetStringInfo(ctx->work_str);
+       }
 
        ctx->last_elem_is_object = true;
        ctx->level--;
@@ -631,6 +668,18 @@ json_text_ofstart(void *state, char *fname, bool isnull)
                        print_current_node(ctx);
                        clear_nodeval(ctx->nodevals);
                }
+               else if (p->tag == P_TargetTables)
+               {
+                       node_vals *v = ctx->nodevals;
+
+                       ctx->current_list = p->tag;
+                       ctx->list_fname = fname;
+
+                       /* stash some data */
+                       v->tmp_obj_name = v->obj_name;
+                       v->tmp_schema_name = v->schema_name;
+                       v->tmp_alias = v->alias;
+               }
 
                /*
                 * This paser prints partial result at the end of every P_Plan object,
@@ -640,7 +689,7 @@ json_text_ofstart(void *state, char *fname, bool isnull)
                        ctx->plan_levels = bms_add_member(ctx->plan_levels, ctx->level);
                else
                        ctx->plan_levels = bms_del_member(ctx->plan_levels, ctx->level);
-               
+
                if (p->tag == P_Plan || p->tag == P_Triggers)
                        ctx->section = p->tag;
                ctx->setter = p->setter;
@@ -653,6 +702,20 @@ json_text_ofend(void *state, char *fname, bool isnull)
        pgspParserContext *ctx = (pgspParserContext *)state;
        node_vals *v = ctx->nodevals;
 
+       /* We assume that lists with same fname will not be nested */
+       if (ctx->list_fname && strcmp(fname, ctx->list_fname) == 0)
+       {
+               /* Restore stashed data, see json_text_ofstart */
+               if (ctx->current_list == P_TargetTables)
+               {
+                       v->obj_name = v->tmp_obj_name;
+                       v->schema_name = v->tmp_schema_name;
+                       v->alias = v->tmp_alias;
+               }
+               ctx->list_fname = NULL;
+               ctx->current_list = P_Invalid;
+       }
+
        /* Planning/Execution time to be appeared at the end of plan */
        if (HASSTRING(v->plan_time) ||
                HASSTRING(v->exec_time))
index 4aa1518..202ebdc 100644 (file)
@@ -27,6 +27,7 @@ typedef struct
        const char *rows_removed_by_filter;
        const char *alias;
        StringInfo output;
+       List       *target_tables;
        const char *func_call;
        const char *sort_method;
        StringInfo sort_key;
@@ -76,6 +77,10 @@ typedef struct
        const char *exact_heap_blks;
        const char *lossy_heap_blks;
        const char *joinfilt_removed;
+
+       const char *tmp_obj_name;
+       const char *tmp_schema_name;
+       const char *tmp_alias;
 } node_vals;
 
 #define SETTER(name) pgsp_node_set_##name