1 /*-------------------------------------------------------------------------
3 * pgsp_json_text.h: Text plan generator for pg_store_plan.
5 * Copyright (c) 2012-2015, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
8 * pg_store_plan/pgsp_json_text.c
10 *-------------------------------------------------------------------------
14 #include "miscadmin.h"
15 #include "nodes/nodes.h"
16 #include "nodes/bitmapset.h"
17 #include "utils/json.h"
18 #include "utils/jsonapi.h"
19 #include "utils/builtins.h"
21 #include "pgsp_json_text.h"
22 #include "pgsp_json_int.h"
24 static void clear_nodeval(node_vals *vals);
25 static void print_current_node(pgspParserContext *ctx);
26 static void print_current_trig_node(pgspParserContext *ctx);
27 static void print_prop(StringInfo s, char *prepstr,
28 const char *prop, int leve, int exind);
29 static void print_prop_if_exists(StringInfo s, char *prepstr,
30 const char *prop, int leve, int exind);
31 static void print_prop_if_nz(StringInfo s, char *prepstr,
32 const char *prop, int leve, int exind);
33 static void json_text_objstart(void *state);
34 static void json_text_objend(void *state);
35 static void json_text_arrstart(void *state);
36 static void json_text_arrend(void *state);
37 static void json_text_ofstart(void *state, char *fname, bool isnull);
38 static void json_text_ofend(void *state, char *fname, bool isnull);
39 static void json_text_scalar(void *state, char *token, JsonTokenType tokentype);
41 /* Parser callbacks for plan textization */
46 vals->node_type = val;
47 vals->nodetag = T_Invalid;
49 p = search_word_table(nodetypes, val, PGSP_JSON_TEXTIZE);
52 vals->node_type = (p->textname ? p->textname : p->longname);
53 vals->nodetag = p->tag;
61 p = search_word_table(strategies, val, PGSP_JSON_TEXTIZE);
63 switch (vals->nodetag)
69 vals->node_type = "HashAggregate"; break;
71 vals->node_type = "GroupAggregate"; break;
78 if (p->tag == S_Hashed)
79 vals->node_type = "HashSetOp";
86 CONVERSION_SETTER(scan_dir, conv_scandir);
87 SQLQUOTE_SETTER(obj_name);
88 SQLQUOTE_SETTER(alias);
89 SQLQUOTE_SETTER(schema_name);
91 DEFAULT_SETTER(merge_cond);
92 CONVERSION_SETTER(join_type, conv_jointype);
93 CONVERSION_SETTER(setopcommand, conv_setsetopcommand);
94 CONVERSION_SETTER(sort_method, conv_sortmethod);
95 LIST_SETTER(sort_key);
96 LIST_SETTER(group_key);
97 SQLQUOTE_SETTER(index_name);
98 DEFAULT_SETTER(startup_cost);
99 DEFAULT_SETTER(total_cost);
100 DEFAULT_SETTER(plan_rows);
101 DEFAULT_SETTER(plan_width);
102 DEFAULT_SETTER(sort_space_used);
103 CONVERSION_SETTER(sort_space_type, conv_sortspacetype);
104 DEFAULT_SETTER(filter);
105 DEFAULT_SETTER(join_filter);
106 DEFAULT_SETTER(func_call);
107 DEFAULT_SETTER(index_cond);
108 DEFAULT_SETTER(recheck_cond);
109 CONVERSION_SETTER(operation, conv_operation);
110 DEFAULT_SETTER(subplan_name);
111 DEFAULT_SETTER(hash_cond);
112 DEFAULT_SETTER(tid_cond);
113 DEFAULT_SETTER(filter_removed);
114 DEFAULT_SETTER(idxrchk_removed);
115 DEFAULT_SETTER(peak_memory_usage);
116 DEFAULT_SETTER(org_hash_batches);
117 DEFAULT_SETTER(org_hash_buckets);
118 DEFAULT_SETTER(hash_batches);
119 DEFAULT_SETTER(hash_buckets);
120 DEFAULT_SETTER(actual_startup_time);
121 DEFAULT_SETTER(actual_total_time);
122 DEFAULT_SETTER(actual_rows);
123 DEFAULT_SETTER(actual_loops);
124 DEFAULT_SETTER(heap_fetches);
125 DEFAULT_SETTER(shared_hit_blks);
126 DEFAULT_SETTER(shared_read_blks);
127 DEFAULT_SETTER(shared_dirtied_blks);
128 DEFAULT_SETTER(shared_written_blks);
129 DEFAULT_SETTER(local_hit_blks);
130 DEFAULT_SETTER(local_read_blks);
131 DEFAULT_SETTER(local_dirtied_blks);
132 DEFAULT_SETTER(local_written_blks);
133 DEFAULT_SETTER(temp_read_blks);
134 DEFAULT_SETTER(temp_written_blks);
135 DEFAULT_SETTER(io_read_time);
136 DEFAULT_SETTER(io_write_time);
137 SQLQUOTE_SETTER(trig_name);
138 SQLQUOTE_SETTER(trig_relation);
139 DEFAULT_SETTER(trig_time);
140 DEFAULT_SETTER(trig_calls);
141 DEFAULT_SETTER(plan_time);
142 DEFAULT_SETTER(exec_time);
143 DEFAULT_SETTER(exact_heap_blks);
144 DEFAULT_SETTER(lossy_heap_blks);
145 DEFAULT_SETTER(joinfilt_removed);
146 DEFAULT_SETTER(conflict_resolution);
147 LIST_SETTER(conflict_arbiter_indexes);
148 DEFAULT_SETTER(tuples_inserted);
149 DEFAULT_SETTER(conflicting_tuples);
152 #define ISZERO(s) (!s || strcmp(s, "0") == 0 || strcmp(s, "0.000") == 0 )
153 #define HASSTRING(s) (s && strlen(s) > 0)
154 #define TEXT_LEVEL_STEP 6
155 #define TEXT_INDENT_OFFSET 2
156 #define TEXT_INDENT_BASE(l, e) \
157 (((l < 2) ? 0 : (TEXT_LEVEL_STEP * (l - 2) + TEXT_INDENT_OFFSET)) + e)
158 #define TEXT_INDENT_DETAILS(l, e) \
159 (TEXT_INDENT_BASE(l, e) + ((l < 2) ? 2 : 6))
162 print_obj_name0(StringInfo s,
163 const char *obj_name, const char *schema_name, const char *alias)
165 bool on_written = false;
167 if (HASSTRING(obj_name))
170 appendStringInfoString(s, " on ");
171 if (HASSTRING(schema_name))
173 appendStringInfoString(s, schema_name);
174 appendStringInfoChar(s, '.');
176 appendStringInfoString(s, obj_name);
178 if (HASSTRING(alias) &&
179 (!HASSTRING(obj_name) || strcmp(obj_name, alias) != 0))
182 appendStringInfoString(s, " on ");
184 appendStringInfoChar(s, ' ');
185 appendStringInfoString(s, alias);
190 print_obj_name(pgspParserContext *ctx)
192 node_vals *v = ctx->nodevals;
193 StringInfo s = ctx->dest;
195 print_obj_name0(s, v->obj_name, v->schema_name, v->alias);
199 print_prop(StringInfo s, char *prepstr,
200 const char *prop, int level, int exind)
204 appendStringInfoString(s, "\n");
205 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
207 appendStringInfoString(s, prepstr);
208 appendStringInfoString(s, prop);
212 print_prop_if_exists(StringInfo s, char *prepstr,
213 const char *prop, int level, int exind)
216 print_prop(s, prepstr, prop, level, exind);
220 print_propstr_if_exists(StringInfo s, char *prepstr,
221 StringInfo prop, int level, int exind)
223 if (prop && prop->data[0])
225 appendStringInfoString(s, "\n");
226 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
227 appendStringInfoString(s, prepstr);
228 appendStringInfoString(s, prop->data);
233 print_groupingsets_if_exists(StringInfo s, List *gss, int level, int exind)
240 grouping_set *gs = (grouping_set *)lfirst (lc);
244 print_prop_if_exists(s, "Sort Key: ", gs->sort_keys, level, exind);
248 foreach (lcg, gs->group_keys)
250 const char *gk = (const char *)lfirst (lcg);
251 print_prop_if_exists(s, "Group Key: ", gk, level, exind);
257 print_prop_if_nz(StringInfo s, char *prepstr,
258 const char *prop, int level, int exind)
261 print_prop(s, prepstr, prop, level, exind);
265 print_current_node(pgspParserContext *ctx)
267 node_vals *v = ctx->nodevals;
268 StringInfo s = ctx->dest;
270 int level = ctx->level - 1;
274 if (v->node_type == T_Invalid)
278 appendStringInfoString(s, "\n");
279 appendStringInfoSpaces(s, TEXT_INDENT_BASE(level, exind));
281 if (HASSTRING(v->subplan_name))
283 appendStringInfoString(s, v->subplan_name);
284 appendStringInfoString(s, "\n");
286 appendStringInfoSpaces(s, TEXT_INDENT_BASE(level, exind));
289 /* list items doesn't need this header */
290 if (level > 1 && ctx->current_list == P_Invalid)
291 appendStringInfoString(s, "-> ");
297 case T_BitmapHeapScan:
303 case T_WorkTableScan:
305 if (v->nodetag == T_ModifyTable)
306 appendStringInfoString(s, v->operation);
308 appendStringInfoString(s, v->node_type);
314 case T_IndexOnlyScan:
315 case T_BitmapIndexScan:
316 appendStringInfoString(s, v->node_type);
317 print_prop_if_exists(s, " ", v->scan_dir, 0, 0);
318 print_prop_if_exists(s, " using ", v->index_name, 0, 0);
325 appendStringInfoString(s, v->node_type);
326 if (v->join_type && strcmp(v->join_type, "Inner") != 0)
328 appendStringInfoChar(s, ' ');
329 appendStringInfoString(s, v->join_type);
331 if (v->nodetag != T_NestLoop)
332 appendStringInfoString(s, " Join");
336 appendStringInfoString(s, v->node_type);
337 print_prop_if_exists(s, " ", v->setopcommand, 0, 0);
341 appendStringInfoString(s, v->node_type);
345 /* Don't show costs for child talbes */
346 if (ctx->current_list == P_TargetTables)
349 if (!ISZERO(v->startup_cost) &&
350 !ISZERO(v->total_cost) &&
351 HASSTRING(v->plan_rows) &&
352 HASSTRING(v->plan_width))
354 appendStringInfoString(s, " (cost=");
355 appendStringInfoString(s, v->startup_cost);
356 appendStringInfoString(s, "..");
357 appendStringInfoString(s, v->total_cost);
358 appendStringInfoString(s, " rows=");
359 appendStringInfoString(s, v->plan_rows);
360 appendStringInfoString(s, " width=");
361 appendStringInfoString(s, v->plan_width);
362 appendStringInfoString(s, ")");
365 if (HASSTRING(v->actual_loops) && ISZERO(v->actual_loops))
366 appendStringInfoString(s, " (never executed)");
367 else if (HASSTRING(v->actual_rows) &&
368 HASSTRING(v->actual_loops) &&
369 HASSTRING(v->actual_startup_time) &&
370 HASSTRING(v->actual_total_time))
372 appendStringInfoString(s, " (actual ");
373 appendStringInfoString(s, "time=");
374 appendStringInfoString(s, v->actual_startup_time);
375 appendStringInfoString(s, "..");
376 appendStringInfoString(s, v->actual_total_time);
377 appendStringInfoString(s, " ");
379 appendStringInfoString(s, "rows=");
380 appendStringInfoString(s, v->actual_rows);
382 appendStringInfoString(s, " loops=");
383 appendStringInfoString(s, v->actual_loops);
385 appendStringInfoString(s, ")");
388 foreach(lc, v->target_tables)
390 char *str = (char *)lfirst (lc);
392 appendStringInfoString(s, "\n");
393 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
394 appendStringInfoString(s, str);
397 print_propstr_if_exists(s, "Output: ", v->output, level, exind);
398 print_propstr_if_exists(s, "Group Key: ", v->group_key, level, exind);
399 print_groupingsets_if_exists(s, v->grouping_sets, level, exind);
400 print_prop_if_exists(s, "Merge Cond: ", v->merge_cond, level, exind);
401 print_prop_if_exists(s, "Hash Cond: " , v->hash_cond, level, exind);
402 print_prop_if_exists(s, "Tid Cond: " , v->tid_cond, level, exind);
403 print_prop_if_exists(s, "Join Filter: " , v->join_filter, level, exind);
404 print_prop_if_exists(s, "Index Cond: " , v->index_cond, level, exind);
405 print_prop_if_exists(s, "Recheck Cond: ", v->recheck_cond, level, exind);
406 print_propstr_if_exists(s, "Sort Key: ", v->sort_key, level, exind);
408 if (HASSTRING(v->sort_method))
410 appendStringInfoString(s, "\n");
411 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
412 appendStringInfoString(s, "Sort Method: ");
413 appendStringInfoString(s, v->sort_method);
415 if (HASSTRING(v->sort_space_type) &&
416 HASSTRING(v->sort_space_used))
418 appendStringInfoString(s, " ");
419 appendStringInfoString(s, v->sort_space_type);
420 appendStringInfoString(s, ": ");
421 appendStringInfoString(s, v->sort_space_used);
422 appendStringInfoString(s, "kB");
426 print_prop_if_exists(s, "Function Call: ", v->func_call, level, exind);
427 print_prop_if_exists(s, "Filter: ", v->filter, level, exind);
428 print_prop_if_nz(s, "Rows Removed by Filter: ",
429 v->filter_removed, level, exind);
430 print_prop_if_nz(s, "Rows Removed by Index Recheck: ",
431 v->idxrchk_removed, level, exind);
432 print_prop_if_nz(s, "Rows Removed by Join Filter: ",
433 v->joinfilt_removed, level, exind);
435 if (HASSTRING(v->exact_heap_blks) ||
436 HASSTRING(v->lossy_heap_blks))
438 appendStringInfoString(s, "\n");
439 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
440 appendStringInfoString(s, "Heap Blocks:");
441 print_prop_if_nz(s, " exact=", v->exact_heap_blks, 0, exind);
442 print_prop_if_nz(s, " lossy=", v->lossy_heap_blks, 0, exind);
445 if (!ISZERO(v->hash_buckets))
447 bool show_original = false;
449 appendStringInfoString(s, "\n");
450 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
451 appendStringInfoString(s, "Buckets: ");
452 appendStringInfoString(s, v->hash_buckets);
454 /* See show_hash_info() in explain.c for details */
455 if ((v->org_hash_buckets &&
456 strcmp(v->hash_buckets, v->org_hash_buckets) != 0) ||
457 (v->org_hash_batches &&
458 strcmp(v->hash_batches, v->org_hash_batches) != 0))
459 show_original = true;
461 if (show_original && v->org_hash_buckets)
463 appendStringInfoString(s, " (originally ");
464 appendStringInfoString(s, v->org_hash_buckets);
465 appendStringInfoChar(s, ')');
468 if (!ISZERO(v->hash_batches))
470 appendStringInfoString(s, " Batches: ");
471 appendStringInfoString(s, v->hash_batches);
472 if (show_original && v->org_hash_batches)
474 appendStringInfoString(s, " (originally ");
475 appendStringInfoString(s, v->org_hash_batches);
476 appendStringInfoChar(s, ')');
479 if (!ISZERO(v->peak_memory_usage))
481 appendStringInfoString(s, " Memory Usage: ");
482 appendStringInfoString(s, v->peak_memory_usage);
483 appendStringInfoString(s, "kB");
487 print_prop_if_exists(s, "Heap Fetches: ", v->heap_fetches, level, exind);
488 print_prop_if_exists(s, "Conflict Resolution: ",
489 v->conflict_resolution, level, exind);
490 print_propstr_if_exists(s, "Conflict Arbiter Indexes: ",
491 v->conflict_arbiter_indexes, level, exind);
492 print_prop_if_exists(s, "Tuples Inserted: ",
493 v->tuples_inserted, level, exind);
494 print_prop_if_exists(s, "Conflicting Tuples: ",
495 v->conflicting_tuples, level, exind);
497 if (!ISZERO(v->shared_hit_blks) ||
498 !ISZERO(v->shared_read_blks) ||
499 !ISZERO(v->shared_dirtied_blks) ||
500 !ISZERO(v->shared_written_blks))
502 appendStringInfoString(s, "\n");
503 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
504 appendStringInfoString(s, "Buffers: shared");
506 if (!ISZERO(v->shared_hit_blks))
508 appendStringInfoString(s, " hit=");
509 appendStringInfoString(s, v->shared_hit_blks);
512 if (!ISZERO(v->shared_read_blks))
514 appendStringInfoString(s, " read=");
515 appendStringInfoString(s, v->shared_read_blks);
518 if (!ISZERO(v->shared_dirtied_blks))
520 appendStringInfoString(s, " dirtied=");
521 appendStringInfoString(s, v->shared_dirtied_blks);
524 if (!ISZERO(v->shared_written_blks))
526 appendStringInfoString(s, " written=");
527 appendStringInfoString(s, v->shared_written_blks);
531 if (!ISZERO(v->local_hit_blks) ||
532 !ISZERO(v->local_read_blks) ||
533 !ISZERO(v->local_dirtied_blks) ||
534 !ISZERO(v->local_written_blks))
537 appendStringInfoString(s, ", ");
540 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
541 appendStringInfoString(s, "Buffers: ");
544 appendStringInfoString(s, "local");
545 if (!ISZERO(v->local_hit_blks))
547 appendStringInfoString(s, " hit=");
548 appendStringInfoString(s, v->local_hit_blks);
551 if (!ISZERO(v->local_read_blks))
553 appendStringInfoString(s, " read=");
554 appendStringInfoString(s, v->local_read_blks);
557 if (!ISZERO(v->local_dirtied_blks))
559 appendStringInfoString(s, " dirtied=");
560 appendStringInfoString(s, v->local_dirtied_blks);
563 if (!ISZERO(v->local_written_blks))
565 appendStringInfoString(s, " written=");
566 appendStringInfoString(s, v->local_written_blks);
570 if (!ISZERO(v->temp_read_blks) ||
571 !ISZERO(v->temp_written_blks))
574 appendStringInfoString(s, ", ");
577 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
578 appendStringInfoString(s, "Buffers: ");
581 appendStringInfoString(s, "temp");
582 if (!ISZERO(v->temp_read_blks))
584 appendStringInfoString(s, " read=");
585 appendStringInfoString(s, v->temp_read_blks);
588 if (!ISZERO(v->temp_written_blks))
590 appendStringInfoString(s, " written=");
591 appendStringInfoString(s, v->temp_written_blks);
595 if (!ISZERO(v->io_read_time) ||
596 !ISZERO(v->io_write_time))
598 /* Feed a line if any of Buffers: items has been shown */
600 appendStringInfoString(s, "\n");
602 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
603 appendStringInfoString(s, "I/O Timings: ");
605 if (!ISZERO(v->io_read_time))
607 appendStringInfoString(s, " read=");
608 appendStringInfoString(s, v->io_read_time);
610 if (!ISZERO(v->io_write_time))
612 appendStringInfoString(s, " write=");
613 appendStringInfoString(s, v->io_write_time);
619 print_current_trig_node(pgspParserContext *ctx)
621 node_vals *v = ctx->nodevals;
622 StringInfo s = ctx->dest;
624 if (HASSTRING(v->trig_name) && !ISZERO(v->trig_time))
627 appendStringInfoString(s, "\n");
628 appendStringInfoString(s, "Trigger ");
629 appendStringInfoString(s, v->trig_name);
630 appendStringInfoString(s, ": time=");
631 appendStringInfoString(s, v->trig_time);
632 appendStringInfoString(s, " calls=");
633 appendStringInfoString(s, v->trig_calls);
639 clear_nodeval(node_vals *vals)
641 memset(vals, 0, sizeof(node_vals));
645 json_text_objstart(void *state)
647 pgspParserContext *ctx = (pgspParserContext *)state;
650 /* Create new grouping sets or reset existing ones */
651 if (ctx->current_list == P_GroupSets)
653 node_vals *v = ctx->nodevals;
655 ctx->tmp_gset = (grouping_set*) palloc0(sizeof(grouping_set));
657 v->sort_key = makeStringInfo();
659 v->group_key = makeStringInfo();
660 resetStringInfo(v->sort_key);
661 resetStringInfo(v->group_key);
666 json_text_objend(void *state)
668 pgspParserContext *ctx = (pgspParserContext *)state;
670 /* Print current node if the object is a P_Plan or a child of P_Plans */
671 if (bms_is_member(ctx->level - 1, ctx->plan_levels))
673 print_current_node(ctx);
674 clear_nodeval(ctx->nodevals);
676 else if (ctx->section == P_Triggers)
678 print_current_trig_node(ctx);
679 clear_nodeval(ctx->nodevals);
681 else if (ctx->current_list == P_TargetTables)
683 /* Move the current working taget tables into nodevals */
684 node_vals *v = ctx->nodevals;
687 ctx->work_str = makeStringInfo();
689 resetStringInfo(ctx->work_str);
690 appendStringInfoString(ctx->work_str, v->operation);
691 print_obj_name0(ctx->work_str, v->obj_name, v->schema_name, v->alias);
692 v->target_tables = lappend(v->target_tables,
693 pstrdup(ctx->work_str->data));
694 resetStringInfo(ctx->work_str);
696 else if (ctx->current_list == P_GroupSets && ctx->tmp_gset)
698 /* Move working grouping set into nodevals */
699 node_vals *v = ctx->nodevals;
701 /* Copy sort key if any */
702 if (v->sort_key->data[0])
704 ctx->tmp_gset->sort_keys = strdup(v->sort_key->data);
705 resetStringInfo(v->sort_key);
708 /* Move working grouping set into nodevals */
709 ctx->nodevals->grouping_sets =
710 lappend(v->grouping_sets, ctx->tmp_gset);
711 ctx->tmp_gset = NULL;
714 ctx->last_elem_is_object = true;
719 json_text_arrstart(void *state)
721 pgspParserContext *ctx = (pgspParserContext *)state;
723 if (ctx->current_list == P_GroupSets)
730 json_text_arrend(void *state)
732 pgspParserContext *ctx = (pgspParserContext *)state;
734 if (ctx->current_list == P_GroupSets)
737 * wlist_level means that now at the end of innermost list of Group
740 if (ctx->wlist_level == 3)
742 node_vals *v = ctx->nodevals;
745 * At this point, v->group_key holds the keys in "Group Keys". The
746 * item holds a double-nested list and the innermost lists are to
747 * go into individual "Group Key" lines. Empty innermost list is
748 * represented as "()" there. See explain.c of PostgreSQL.
750 ctx->tmp_gset->group_keys =
751 lappend(ctx->tmp_gset->group_keys,
752 (v->group_key->data[0] ?
753 pstrdup(v->group_key->data) : "()"));
754 resetStringInfo(ctx->nodevals->group_key);
761 json_text_ofstart(void *state, char *fname, bool isnull)
764 pgspParserContext *ctx = (pgspParserContext *)state;
767 p = search_word_table(propfields, fname, PGSP_JSON_TEXTIZE);
772 (errmsg("Short JSON parser encoutered unknown field name: \"%s\", skipped.", fname),
773 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
778 * Print node immediately if the next level of Plan/Plans comes. The
779 * plan construct is tail-recursive so this doesn't harm.
781 if (p->tag == P_Plan || p->tag == P_Plans)
783 print_current_node(ctx);
784 clear_nodeval(ctx->nodevals);
786 else if (p->tag == P_TargetTables)
788 node_vals *v = ctx->nodevals;
790 ctx->current_list = p->tag;
791 ctx->list_fname = fname;
793 /* stash some data */
794 v->tmp_obj_name = v->obj_name;
795 v->tmp_schema_name = v->schema_name;
796 v->tmp_alias = v->alias;
798 else if (p->tag == P_GroupSets)
800 ctx->current_list = p->tag;
801 ctx->list_fname = fname;
802 ctx->wlist_level = 0;
806 * This paser prints partial result at the end of every P_Plan object,
807 * which includes elements in P_Plans list.
809 if (p->tag == P_Plan || p->tag == P_Plans)
810 ctx->plan_levels = bms_add_member(ctx->plan_levels, ctx->level);
812 ctx->plan_levels = bms_del_member(ctx->plan_levels, ctx->level);
814 if (p->tag == P_Plan || p->tag == P_Triggers)
815 ctx->section = p->tag;
816 ctx->setter = p->setter;
821 json_text_ofend(void *state, char *fname, bool isnull)
823 pgspParserContext *ctx = (pgspParserContext *)state;
824 node_vals *v = ctx->nodevals;
826 /* We assume that lists with same fname will not be nested */
827 if (ctx->list_fname && strcmp(fname, ctx->list_fname) == 0)
829 /* Restore stashed data, see json_text_ofstart */
830 if (ctx->current_list == P_TargetTables)
832 v->obj_name = v->tmp_obj_name;
833 v->schema_name = v->tmp_schema_name;
834 v->alias = v->tmp_alias;
837 ctx->list_fname = NULL;
838 ctx->current_list = P_Invalid;
841 /* Planning/Execution time to be appeared at the end of plan */
842 if (HASSTRING(v->plan_time) ||
843 HASSTRING(v->exec_time))
845 if (HASSTRING(v->plan_time))
847 appendStringInfoString(ctx->dest, "\nPlanning Time: ");
848 appendStringInfoString(ctx->dest, v->plan_time);
849 appendStringInfoString(ctx->dest, " ms");
853 appendStringInfoString(ctx->dest, "\nExecution Time: ");
854 appendStringInfoString(ctx->dest, v->exec_time);
855 appendStringInfoString(ctx->dest, " ms");
862 json_text_scalar(void *state, char *token, JsonTokenType tokentype)
864 pgspParserContext *ctx = (pgspParserContext *)state;
867 ctx->setter(ctx->nodevals, token);
871 pgsp_json_textize(char *json)
875 pgspParserContext ctx;
877 init_json_lex_context(&lex, json);
878 init_parser_context(&ctx, PGSP_JSON_TEXTIZE, json, NULL, 0);
880 ctx.nodevals = (node_vals*)palloc0(sizeof(node_vals));
882 sem.semstate = (void*)&ctx;
883 sem.object_start = json_text_objstart;
884 sem.object_end = json_text_objend;
885 sem.array_start = json_text_arrstart;
886 sem.array_end = json_text_arrend;
887 sem.object_field_start = json_text_ofstart;
888 sem.object_field_end = json_text_ofend;
889 sem.array_element_start= NULL;
890 sem.array_element_end = NULL;
891 sem.scalar = json_text_scalar;
894 if (!run_pg_parse_json(&lex, &sem))
896 if (ctx.nodevals->node_type)
897 print_current_node(&ctx);
899 if (ctx.dest->len > 0 &&
900 ctx.dest->data[ctx.dest->len - 1] != '\n')
901 appendStringInfoChar(ctx.dest, '\n');
903 if (ctx.dest->len == 0)
904 appendStringInfoString(ctx.dest, "<Input was not JSON>");
906 appendStringInfoString(ctx.dest, "<truncated>");
911 return ctx.dest->data;