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);
150 DEFAULT_SETTER(sampling_method);
151 LIST_SETTER(sampling_params);
152 DEFAULT_SETTER(repeatable_seed);
154 #define ISZERO(s) (!s || strcmp(s, "0") == 0 || strcmp(s, "0.000") == 0 )
155 #define HASSTRING(s) (s && strlen(s) > 0)
156 #define TEXT_LEVEL_STEP 6
157 #define TEXT_INDENT_OFFSET 2
158 #define TEXT_INDENT_BASE(l, e) \
159 (((l < 2) ? 0 : (TEXT_LEVEL_STEP * (l - 2) + TEXT_INDENT_OFFSET)) + e)
160 #define TEXT_INDENT_DETAILS(l, e) \
161 (TEXT_INDENT_BASE(l, e) + ((l < 2) ? 2 : 6))
164 print_obj_name0(StringInfo s,
165 const char *obj_name, const char *schema_name, const char *alias)
167 bool on_written = false;
169 if (HASSTRING(obj_name))
172 appendStringInfoString(s, " on ");
173 if (HASSTRING(schema_name))
175 appendStringInfoString(s, schema_name);
176 appendStringInfoChar(s, '.');
178 appendStringInfoString(s, obj_name);
180 if (HASSTRING(alias) &&
181 (!HASSTRING(obj_name) || strcmp(obj_name, alias) != 0))
184 appendStringInfoString(s, " on ");
186 appendStringInfoChar(s, ' ');
187 appendStringInfoString(s, alias);
192 print_obj_name(pgspParserContext *ctx)
194 node_vals *v = ctx->nodevals;
195 StringInfo s = ctx->dest;
197 print_obj_name0(s, v->obj_name, v->schema_name, v->alias);
201 print_prop(StringInfo s, char *prepstr,
202 const char *prop, int level, int exind)
206 appendStringInfoString(s, "\n");
207 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
209 appendStringInfoString(s, prepstr);
210 appendStringInfoString(s, prop);
214 print_prop_if_exists(StringInfo s, char *prepstr,
215 const char *prop, int level, int exind)
218 print_prop(s, prepstr, prop, level, exind);
222 print_propstr_if_exists(StringInfo s, char *prepstr,
223 StringInfo prop, int level, int exind)
225 if (prop && prop->data[0])
227 appendStringInfoString(s, "\n");
228 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
229 appendStringInfoString(s, prepstr);
230 appendStringInfoString(s, prop->data);
235 print_groupingsets_if_exists(StringInfo s, List *gss, int level, int exind)
242 grouping_set *gs = (grouping_set *)lfirst (lc);
246 print_prop_if_exists(s, "Sort Key: ", gs->sort_keys, level, exind);
250 foreach (lcg, gs->group_keys)
252 const char *gk = (const char *)lfirst (lcg);
253 print_prop_if_exists(s, "Group Key: ", gk, level, exind);
259 print_prop_if_nz(StringInfo s, char *prepstr,
260 const char *prop, int level, int exind)
263 print_prop(s, prepstr, prop, level, exind);
267 print_current_node(pgspParserContext *ctx)
269 node_vals *v = ctx->nodevals;
270 StringInfo s = ctx->dest;
272 int level = ctx->level - 1;
276 if (v->node_type == T_Invalid)
280 appendStringInfoString(s, "\n");
281 appendStringInfoSpaces(s, TEXT_INDENT_BASE(level, exind));
283 if (HASSTRING(v->subplan_name))
285 appendStringInfoString(s, v->subplan_name);
286 appendStringInfoString(s, "\n");
288 appendStringInfoSpaces(s, TEXT_INDENT_BASE(level, exind));
291 /* list items doesn't need this header */
292 if (level > 1 && ctx->current_list == P_Invalid)
293 appendStringInfoString(s, "-> ");
299 case T_BitmapHeapScan:
305 case T_WorkTableScan:
307 if (v->nodetag == T_ModifyTable)
308 appendStringInfoString(s, v->operation);
310 appendStringInfoString(s, v->node_type);
316 case T_IndexOnlyScan:
317 case T_BitmapIndexScan:
318 appendStringInfoString(s, v->node_type);
319 print_prop_if_exists(s, " ", v->scan_dir, 0, 0);
320 print_prop_if_exists(s, " using ", v->index_name, 0, 0);
327 appendStringInfoString(s, v->node_type);
328 if (v->join_type && strcmp(v->join_type, "Inner") != 0)
330 appendStringInfoChar(s, ' ');
331 appendStringInfoString(s, v->join_type);
333 if (v->nodetag != T_NestLoop)
334 appendStringInfoString(s, " Join");
338 appendStringInfoString(s, v->node_type);
339 print_prop_if_exists(s, " ", v->setopcommand, 0, 0);
343 appendStringInfoString(s, v->node_type);
347 /* Don't show costs for child talbes */
348 if (ctx->current_list == P_TargetTables)
351 if (!ISZERO(v->startup_cost) &&
352 !ISZERO(v->total_cost) &&
353 HASSTRING(v->plan_rows) &&
354 HASSTRING(v->plan_width))
356 appendStringInfoString(s, " (cost=");
357 appendStringInfoString(s, v->startup_cost);
358 appendStringInfoString(s, "..");
359 appendStringInfoString(s, v->total_cost);
360 appendStringInfoString(s, " rows=");
361 appendStringInfoString(s, v->plan_rows);
362 appendStringInfoString(s, " width=");
363 appendStringInfoString(s, v->plan_width);
364 appendStringInfoString(s, ")");
367 if (HASSTRING(v->actual_loops) && ISZERO(v->actual_loops))
368 appendStringInfoString(s, " (never executed)");
369 else if (HASSTRING(v->actual_rows) &&
370 HASSTRING(v->actual_loops) &&
371 HASSTRING(v->actual_startup_time) &&
372 HASSTRING(v->actual_total_time))
374 appendStringInfoString(s, " (actual ");
375 appendStringInfoString(s, "time=");
376 appendStringInfoString(s, v->actual_startup_time);
377 appendStringInfoString(s, "..");
378 appendStringInfoString(s, v->actual_total_time);
379 appendStringInfoString(s, " ");
381 appendStringInfoString(s, "rows=");
382 appendStringInfoString(s, v->actual_rows);
384 appendStringInfoString(s, " loops=");
385 appendStringInfoString(s, v->actual_loops);
387 appendStringInfoString(s, ")");
390 foreach(lc, v->target_tables)
392 char *str = (char *)lfirst (lc);
394 appendStringInfoString(s, "\n");
395 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
396 appendStringInfoString(s, str);
399 print_propstr_if_exists(s, "Output: ", v->output, level, exind);
400 print_propstr_if_exists(s, "Group Key: ", v->group_key, level, exind);
401 print_groupingsets_if_exists(s, v->grouping_sets, level, exind);
402 print_prop_if_exists(s, "Merge Cond: ", v->merge_cond, level, exind);
403 print_prop_if_exists(s, "Hash Cond: " , v->hash_cond, level, exind);
404 print_prop_if_exists(s, "Tid Cond: " , v->tid_cond, level, exind);
405 print_prop_if_exists(s, "Join Filter: " , v->join_filter, level, exind);
406 print_prop_if_exists(s, "Index Cond: " , v->index_cond, level, exind);
407 print_prop_if_exists(s, "Recheck Cond: ", v->recheck_cond, level, exind);
409 if (HASSTRING(v->sampling_method))
411 appendStringInfoString(s, "\n");
412 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
413 appendStringInfo(s, "Sampling: %s (%s)",
415 v->sampling_params ? v->sampling_params->data : "");
416 if (v->repeatable_seed)
417 appendStringInfo(s, " REPEATABLE (%s)", v->repeatable_seed);
420 print_propstr_if_exists(s, "Sort Key: ", v->sort_key, level, exind);
421 if (HASSTRING(v->sort_method))
423 appendStringInfoString(s, "\n");
424 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
425 appendStringInfoString(s, "Sort Method: ");
426 appendStringInfoString(s, v->sort_method);
428 if (HASSTRING(v->sort_space_type) &&
429 HASSTRING(v->sort_space_used))
431 appendStringInfoString(s, " ");
432 appendStringInfoString(s, v->sort_space_type);
433 appendStringInfoString(s, ": ");
434 appendStringInfoString(s, v->sort_space_used);
435 appendStringInfoString(s, "kB");
439 print_prop_if_exists(s, "Function Call: ", v->func_call, level, exind);
440 print_prop_if_exists(s, "Filter: ", v->filter, level, exind);
441 print_prop_if_nz(s, "Rows Removed by Filter: ",
442 v->filter_removed, level, exind);
443 print_prop_if_nz(s, "Rows Removed by Index Recheck: ",
444 v->idxrchk_removed, level, exind);
445 print_prop_if_nz(s, "Rows Removed by Join Filter: ",
446 v->joinfilt_removed, level, exind);
448 if (HASSTRING(v->exact_heap_blks) ||
449 HASSTRING(v->lossy_heap_blks))
451 appendStringInfoString(s, "\n");
452 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
453 appendStringInfoString(s, "Heap Blocks:");
454 print_prop_if_nz(s, " exact=", v->exact_heap_blks, 0, exind);
455 print_prop_if_nz(s, " lossy=", v->lossy_heap_blks, 0, exind);
458 if (!ISZERO(v->hash_buckets))
460 bool show_original = false;
462 appendStringInfoString(s, "\n");
463 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
464 appendStringInfoString(s, "Buckets: ");
465 appendStringInfoString(s, v->hash_buckets);
467 /* See show_hash_info() in explain.c for details */
468 if ((v->org_hash_buckets &&
469 strcmp(v->hash_buckets, v->org_hash_buckets) != 0) ||
470 (v->org_hash_batches &&
471 strcmp(v->hash_batches, v->org_hash_batches) != 0))
472 show_original = true;
474 if (show_original && v->org_hash_buckets)
476 appendStringInfoString(s, " (originally ");
477 appendStringInfoString(s, v->org_hash_buckets);
478 appendStringInfoChar(s, ')');
481 if (!ISZERO(v->hash_batches))
483 appendStringInfoString(s, " Batches: ");
484 appendStringInfoString(s, v->hash_batches);
485 if (show_original && v->org_hash_batches)
487 appendStringInfoString(s, " (originally ");
488 appendStringInfoString(s, v->org_hash_batches);
489 appendStringInfoChar(s, ')');
492 if (!ISZERO(v->peak_memory_usage))
494 appendStringInfoString(s, " Memory Usage: ");
495 appendStringInfoString(s, v->peak_memory_usage);
496 appendStringInfoString(s, "kB");
500 print_prop_if_exists(s, "Heap Fetches: ", v->heap_fetches, level, exind);
501 print_prop_if_exists(s, "Conflict Resolution: ",
502 v->conflict_resolution, level, exind);
503 print_propstr_if_exists(s, "Conflict Arbiter Indexes: ",
504 v->conflict_arbiter_indexes, level, exind);
505 print_prop_if_exists(s, "Tuples Inserted: ",
506 v->tuples_inserted, level, exind);
507 print_prop_if_exists(s, "Conflicting Tuples: ",
508 v->conflicting_tuples, level, exind);
510 if (!ISZERO(v->shared_hit_blks) ||
511 !ISZERO(v->shared_read_blks) ||
512 !ISZERO(v->shared_dirtied_blks) ||
513 !ISZERO(v->shared_written_blks))
515 appendStringInfoString(s, "\n");
516 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
517 appendStringInfoString(s, "Buffers: shared");
519 if (!ISZERO(v->shared_hit_blks))
521 appendStringInfoString(s, " hit=");
522 appendStringInfoString(s, v->shared_hit_blks);
525 if (!ISZERO(v->shared_read_blks))
527 appendStringInfoString(s, " read=");
528 appendStringInfoString(s, v->shared_read_blks);
531 if (!ISZERO(v->shared_dirtied_blks))
533 appendStringInfoString(s, " dirtied=");
534 appendStringInfoString(s, v->shared_dirtied_blks);
537 if (!ISZERO(v->shared_written_blks))
539 appendStringInfoString(s, " written=");
540 appendStringInfoString(s, v->shared_written_blks);
544 if (!ISZERO(v->local_hit_blks) ||
545 !ISZERO(v->local_read_blks) ||
546 !ISZERO(v->local_dirtied_blks) ||
547 !ISZERO(v->local_written_blks))
550 appendStringInfoString(s, ", ");
553 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
554 appendStringInfoString(s, "Buffers: ");
557 appendStringInfoString(s, "local");
558 if (!ISZERO(v->local_hit_blks))
560 appendStringInfoString(s, " hit=");
561 appendStringInfoString(s, v->local_hit_blks);
564 if (!ISZERO(v->local_read_blks))
566 appendStringInfoString(s, " read=");
567 appendStringInfoString(s, v->local_read_blks);
570 if (!ISZERO(v->local_dirtied_blks))
572 appendStringInfoString(s, " dirtied=");
573 appendStringInfoString(s, v->local_dirtied_blks);
576 if (!ISZERO(v->local_written_blks))
578 appendStringInfoString(s, " written=");
579 appendStringInfoString(s, v->local_written_blks);
583 if (!ISZERO(v->temp_read_blks) ||
584 !ISZERO(v->temp_written_blks))
587 appendStringInfoString(s, ", ");
590 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
591 appendStringInfoString(s, "Buffers: ");
594 appendStringInfoString(s, "temp");
595 if (!ISZERO(v->temp_read_blks))
597 appendStringInfoString(s, " read=");
598 appendStringInfoString(s, v->temp_read_blks);
601 if (!ISZERO(v->temp_written_blks))
603 appendStringInfoString(s, " written=");
604 appendStringInfoString(s, v->temp_written_blks);
608 if (!ISZERO(v->io_read_time) ||
609 !ISZERO(v->io_write_time))
611 /* Feed a line if any of Buffers: items has been shown */
613 appendStringInfoString(s, "\n");
615 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
616 appendStringInfoString(s, "I/O Timings: ");
618 if (!ISZERO(v->io_read_time))
620 appendStringInfoString(s, " read=");
621 appendStringInfoString(s, v->io_read_time);
623 if (!ISZERO(v->io_write_time))
625 appendStringInfoString(s, " write=");
626 appendStringInfoString(s, v->io_write_time);
632 print_current_trig_node(pgspParserContext *ctx)
634 node_vals *v = ctx->nodevals;
635 StringInfo s = ctx->dest;
637 if (HASSTRING(v->trig_name) && !ISZERO(v->trig_time))
640 appendStringInfoString(s, "\n");
641 appendStringInfoString(s, "Trigger ");
642 appendStringInfoString(s, v->trig_name);
643 appendStringInfoString(s, ": time=");
644 appendStringInfoString(s, v->trig_time);
645 appendStringInfoString(s, " calls=");
646 appendStringInfoString(s, v->trig_calls);
652 clear_nodeval(node_vals *vals)
654 memset(vals, 0, sizeof(node_vals));
658 json_text_objstart(void *state)
660 pgspParserContext *ctx = (pgspParserContext *)state;
663 /* Create new grouping sets or reset existing ones */
664 if (ctx->current_list == P_GroupSets)
666 node_vals *v = ctx->nodevals;
668 ctx->tmp_gset = (grouping_set*) palloc0(sizeof(grouping_set));
670 v->sort_key = makeStringInfo();
672 v->group_key = makeStringInfo();
673 resetStringInfo(v->sort_key);
674 resetStringInfo(v->group_key);
679 json_text_objend(void *state)
681 pgspParserContext *ctx = (pgspParserContext *)state;
683 /* Print current node if the object is a P_Plan or a child of P_Plans */
684 if (bms_is_member(ctx->level - 1, ctx->plan_levels))
686 print_current_node(ctx);
687 clear_nodeval(ctx->nodevals);
689 else if (ctx->section == P_Triggers)
691 print_current_trig_node(ctx);
692 clear_nodeval(ctx->nodevals);
694 else if (ctx->current_list == P_TargetTables)
696 /* Move the current working taget tables into nodevals */
697 node_vals *v = ctx->nodevals;
700 ctx->work_str = makeStringInfo();
702 resetStringInfo(ctx->work_str);
703 appendStringInfoString(ctx->work_str, v->operation);
704 print_obj_name0(ctx->work_str, v->obj_name, v->schema_name, v->alias);
705 v->target_tables = lappend(v->target_tables,
706 pstrdup(ctx->work_str->data));
707 resetStringInfo(ctx->work_str);
709 else if (ctx->current_list == P_GroupSets && ctx->tmp_gset)
711 /* Move working grouping set into nodevals */
712 node_vals *v = ctx->nodevals;
714 /* Copy sort key if any */
715 if (v->sort_key->data[0])
717 ctx->tmp_gset->sort_keys = strdup(v->sort_key->data);
718 resetStringInfo(v->sort_key);
721 /* Move working grouping set into nodevals */
722 ctx->nodevals->grouping_sets =
723 lappend(v->grouping_sets, ctx->tmp_gset);
724 ctx->tmp_gset = NULL;
727 ctx->last_elem_is_object = true;
732 json_text_arrstart(void *state)
734 pgspParserContext *ctx = (pgspParserContext *)state;
736 if (ctx->current_list == P_GroupSets)
743 json_text_arrend(void *state)
745 pgspParserContext *ctx = (pgspParserContext *)state;
747 if (ctx->current_list == P_GroupSets)
750 * wlist_level means that now at the end of innermost list of Group
753 if (ctx->wlist_level == 3)
755 node_vals *v = ctx->nodevals;
758 * At this point, v->group_key holds the keys in "Group Keys". The
759 * item holds a double-nested list and the innermost lists are to
760 * go into individual "Group Key" lines. Empty innermost list is
761 * represented as "()" there. See explain.c of PostgreSQL.
763 ctx->tmp_gset->group_keys =
764 lappend(ctx->tmp_gset->group_keys,
765 (v->group_key->data[0] ?
766 pstrdup(v->group_key->data) : "()"));
767 resetStringInfo(ctx->nodevals->group_key);
774 json_text_ofstart(void *state, char *fname, bool isnull)
777 pgspParserContext *ctx = (pgspParserContext *)state;
780 p = search_word_table(propfields, fname, PGSP_JSON_TEXTIZE);
785 (errmsg("Short JSON parser encoutered unknown field name: \"%s\", skipped.", fname),
786 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
791 * Print node immediately if the next level of Plan/Plans comes. The
792 * plan construct is tail-recursive so this doesn't harm.
794 if (p->tag == P_Plan || p->tag == P_Plans)
796 print_current_node(ctx);
797 clear_nodeval(ctx->nodevals);
799 else if (p->tag == P_TargetTables)
801 node_vals *v = ctx->nodevals;
803 ctx->current_list = p->tag;
804 ctx->list_fname = fname;
806 /* stash some data */
807 v->tmp_obj_name = v->obj_name;
808 v->tmp_schema_name = v->schema_name;
809 v->tmp_alias = v->alias;
811 else if (p->tag == P_GroupSets)
813 ctx->current_list = p->tag;
814 ctx->list_fname = fname;
815 ctx->wlist_level = 0;
819 * This paser prints partial result at the end of every P_Plan object,
820 * which includes elements in P_Plans list.
822 if (p->tag == P_Plan || p->tag == P_Plans)
823 ctx->plan_levels = bms_add_member(ctx->plan_levels, ctx->level);
825 ctx->plan_levels = bms_del_member(ctx->plan_levels, ctx->level);
827 if (p->tag == P_Plan || p->tag == P_Triggers)
828 ctx->section = p->tag;
829 ctx->setter = p->setter;
834 json_text_ofend(void *state, char *fname, bool isnull)
836 pgspParserContext *ctx = (pgspParserContext *)state;
837 node_vals *v = ctx->nodevals;
839 /* We assume that lists with same fname will not be nested */
840 if (ctx->list_fname && strcmp(fname, ctx->list_fname) == 0)
842 /* Restore stashed data, see json_text_ofstart */
843 if (ctx->current_list == P_TargetTables)
845 v->obj_name = v->tmp_obj_name;
846 v->schema_name = v->tmp_schema_name;
847 v->alias = v->tmp_alias;
850 ctx->list_fname = NULL;
851 ctx->current_list = P_Invalid;
854 /* Planning/Execution time to be appeared at the end of plan */
855 if (HASSTRING(v->plan_time) ||
856 HASSTRING(v->exec_time))
858 if (HASSTRING(v->plan_time))
860 appendStringInfoString(ctx->dest, "\nPlanning Time: ");
861 appendStringInfoString(ctx->dest, v->plan_time);
862 appendStringInfoString(ctx->dest, " ms");
866 appendStringInfoString(ctx->dest, "\nExecution Time: ");
867 appendStringInfoString(ctx->dest, v->exec_time);
868 appendStringInfoString(ctx->dest, " ms");
875 json_text_scalar(void *state, char *token, JsonTokenType tokentype)
877 pgspParserContext *ctx = (pgspParserContext *)state;
880 ctx->setter(ctx->nodevals, token);
884 pgsp_json_textize(char *json)
888 pgspParserContext ctx;
890 init_json_lex_context(&lex, json);
891 init_parser_context(&ctx, PGSP_JSON_TEXTIZE, json, NULL, 0);
893 ctx.nodevals = (node_vals*)palloc0(sizeof(node_vals));
895 sem.semstate = (void*)&ctx;
896 sem.object_start = json_text_objstart;
897 sem.object_end = json_text_objend;
898 sem.array_start = json_text_arrstart;
899 sem.array_end = json_text_arrend;
900 sem.object_field_start = json_text_ofstart;
901 sem.object_field_end = json_text_ofend;
902 sem.array_element_start= NULL;
903 sem.array_element_end = NULL;
904 sem.scalar = json_text_scalar;
907 if (!run_pg_parse_json(&lex, &sem))
909 if (ctx.nodevals->node_type)
910 print_current_node(&ctx);
912 if (ctx.dest->len > 0 &&
913 ctx.dest->data[ctx.dest->len - 1] != '\n')
914 appendStringInfoChar(ctx.dest, '\n');
916 if (ctx.dest->len == 0)
917 appendStringInfoString(ctx.dest, "<Input was not JSON>");
919 appendStringInfoString(ctx.dest, "<truncated>");
924 return ctx.dest->data;