1 /*-------------------------------------------------------------------------
3 * pgsp_json_text.h: Text plan generator for pg_store_plan.
5 * Copyright (c) 2012-2016, 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 "nodes/pg_list.h"
18 #include "utils/json.h"
19 #include "utils/jsonapi.h"
20 #include "utils/builtins.h"
22 #include "pgsp_json_text.h"
23 #include "pgsp_json_int.h"
25 static void clear_nodeval(node_vals *vals);
26 static void print_current_node(pgspParserContext *ctx);
27 static void print_current_trig_node(pgspParserContext *ctx);
28 static void print_prop(StringInfo s, char *prepstr,
29 const char *prop, int leve, int exind);
30 static void print_prop_if_exists(StringInfo s, char *prepstr,
31 const char *prop, int leve, int exind);
32 static void print_prop_if_nz(StringInfo s, char *prepstr,
33 const char *prop, int leve, int exind);
34 static void json_text_objstart(void *state);
35 static void json_text_objend(void *state);
36 static void json_text_arrstart(void *state);
37 static void json_text_arrend(void *state);
38 static void json_text_ofstart(void *state, char *fname, bool isnull);
39 static void json_text_ofend(void *state, char *fname, bool isnull);
40 static void json_text_scalar(void *state, char *token, JsonTokenType tokentype);
42 /* Parser callbacks for plan textization */
45 * This setter is used for field names that store_plans doesn't know of.
46 * Unlike the other setters, this holds a list of strings emitted as is in text
53 if(vals->_undef_newelem)
56 vals->_undef = lappend(vals->_undef, s);
60 s = llast (vals->_undef);
63 appendStringInfoString(s, val);
70 vals->node_type = val;
71 vals->nodetag = T_Invalid;
73 p = search_word_table(nodetypes, val, PGSP_JSON_TEXTIZE);
76 vals->node_type = (p->textname ? p->textname : p->longname);
77 vals->nodetag = p->tag;
85 p = search_word_table(strategies, val, PGSP_JSON_TEXTIZE);
90 switch (vals->nodetag)
96 vals->node_type = "HashAggregate"; break;
98 vals->node_type = "GroupAggregate"; break;
100 vals->node_type = "MixedAggregate"; break;
107 if (p->tag == S_Hashed)
108 vals->node_type = "HashSetOp";
115 CONVERSION_SETTER(scan_dir, conv_scandir);
116 SQLQUOTE_SETTER(obj_name);
117 SQLQUOTE_SETTER(alias);
118 SQLQUOTE_SETTER(schema_name);
120 DEFAULT_SETTER(merge_cond);
121 CONVERSION_SETTER(join_type, conv_jointype);
122 CONVERSION_SETTER(setopcommand, conv_setsetopcommand);
123 CONVERSION_SETTER(sort_method, conv_sortmethod);
124 LIST_SETTER(sort_key);
125 LIST_SETTER(group_key);
126 LIST_SETTER(hash_key);
127 BOOL_SETTER(parallel_aware);
128 CONVERSION_SETTER(partial_mode, conv_partialmode);
129 SQLQUOTE_SETTER(index_name);
130 DEFAULT_SETTER(startup_cost);
131 DEFAULT_SETTER(total_cost);
132 DEFAULT_SETTER(plan_rows);
133 DEFAULT_SETTER(plan_width);
134 DEFAULT_SETTER(sort_space_used);
135 CONVERSION_SETTER(sort_space_type, conv_sortspacetype);
136 DEFAULT_SETTER(filter);
137 DEFAULT_SETTER(join_filter);
138 DEFAULT_SETTER(func_call);
139 DEFAULT_SETTER(index_cond);
140 DEFAULT_SETTER(recheck_cond);
141 CONVERSION_SETTER(operation, conv_operation);
142 DEFAULT_SETTER(subplan_name);
143 DEFAULT_SETTER(hash_cond);
144 DEFAULT_SETTER(tid_cond);
145 DEFAULT_SETTER(filter_removed);
146 DEFAULT_SETTER(idxrchk_removed);
147 DEFAULT_SETTER(peak_memory_usage);
148 DEFAULT_SETTER(org_hash_batches);
149 DEFAULT_SETTER(org_hash_buckets);
150 DEFAULT_SETTER(hash_batches);
151 DEFAULT_SETTER(hash_buckets);
152 DEFAULT_SETTER(actual_startup_time);
153 DEFAULT_SETTER(actual_total_time);
154 DEFAULT_SETTER(actual_rows);
155 DEFAULT_SETTER(actual_loops);
156 DEFAULT_SETTER(heap_fetches);
157 DEFAULT_SETTER(shared_hit_blks);
158 DEFAULT_SETTER(shared_read_blks);
159 DEFAULT_SETTER(shared_dirtied_blks);
160 DEFAULT_SETTER(shared_written_blks);
161 DEFAULT_SETTER(local_hit_blks);
162 DEFAULT_SETTER(local_read_blks);
163 DEFAULT_SETTER(local_dirtied_blks);
164 DEFAULT_SETTER(local_written_blks);
165 DEFAULT_SETTER(temp_read_blks);
166 DEFAULT_SETTER(temp_written_blks);
167 DEFAULT_SETTER(io_read_time);
168 DEFAULT_SETTER(io_write_time);
169 SQLQUOTE_SETTER(trig_name);
170 SQLQUOTE_SETTER(trig_relation);
171 DEFAULT_SETTER(trig_time);
172 DEFAULT_SETTER(trig_calls);
173 DEFAULT_SETTER(plan_time);
174 DEFAULT_SETTER(exec_time);
175 DEFAULT_SETTER(exact_heap_blks);
176 DEFAULT_SETTER(lossy_heap_blks);
177 DEFAULT_SETTER(joinfilt_removed);
178 DEFAULT_SETTER(conflict_resolution);
179 LIST_SETTER(conflict_arbiter_indexes);
180 DEFAULT_SETTER(tuples_inserted);
181 DEFAULT_SETTER(conflicting_tuples);
182 DEFAULT_SETTER(sampling_method);
183 LIST_SETTER(sampling_params);
184 DEFAULT_SETTER(repeatable_seed);
185 DEFAULT_SETTER(worker_number);
186 DEFAULT_SETTER(workers_planned);
187 DEFAULT_SETTER(workers_launched);
188 BOOL_SETTER(inner_unique);
189 DEFAULT_SETTER(table_func_name);
191 #define ISZERO(s) (!s || strcmp(s, "0") == 0 || strcmp(s, "0.000") == 0 )
192 #define HASSTRING(s) (s && strlen(s) > 0)
193 #define TEXT_LEVEL_STEP 6
194 #define TEXT_INDENT_OFFSET 2
195 #define TEXT_INDENT_BASE(l, e) \
196 (((l < 2) ? 0 : (TEXT_LEVEL_STEP * (l - 2) + TEXT_INDENT_OFFSET)) + e)
197 #define TEXT_INDENT_DETAILS(l, e) \
198 (TEXT_INDENT_BASE(l, e) + ((l < 2) ? 2 : 6))
201 print_obj_name0(StringInfo s,
202 const char *obj_name, const char *schema_name, const char *alias)
204 bool on_written = false;
206 if (HASSTRING(obj_name))
209 appendStringInfoString(s, " on ");
210 if (HASSTRING(schema_name))
212 appendStringInfoString(s, schema_name);
213 appendStringInfoChar(s, '.');
215 appendStringInfoString(s, obj_name);
217 if (HASSTRING(alias) &&
218 (!HASSTRING(obj_name) || strcmp(obj_name, alias) != 0))
221 appendStringInfoString(s, " on ");
223 appendStringInfoChar(s, ' ');
224 appendStringInfoString(s, alias);
229 print_obj_name(pgspParserContext *ctx)
231 node_vals *v = ctx->nodevals;
232 StringInfo s = ctx->dest;
234 print_obj_name0(s, v->obj_name, v->schema_name, v->alias);
238 print_prop(StringInfo s, char *prepstr,
239 const char *prop, int level, int exind)
243 appendStringInfoString(s, "\n");
244 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
246 appendStringInfoString(s, prepstr);
247 appendStringInfoString(s, prop);
251 print_prop_if_exists(StringInfo s, char *prepstr,
252 const char *prop, int level, int exind)
255 print_prop(s, prepstr, prop, level, exind);
259 print_propstr_if_exists(StringInfo s, char *prepstr,
260 StringInfo prop, int level, int exind)
262 if (prop && prop->data[0])
264 appendStringInfoString(s, "\n");
265 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
266 appendStringInfoString(s, prepstr);
267 appendStringInfoString(s, prop->data);
272 print_groupingsets_if_exists(StringInfo s, List *gss, int level, int exind)
279 grouping_set *gs = (grouping_set *)lfirst (lc);
283 print_prop_if_exists(s, "Sort Key: ", gs->sort_keys, level, exind);
287 foreach (lcg, gs->group_keys)
289 const char *gk = (const char *)lfirst (lcg);
290 print_prop_if_exists(s, gs->key_type, gk, level, exind);
297 print_prop_if_nz(StringInfo s, char *prepstr,
298 const char *prop, int level, int exind)
301 print_prop(s, prepstr, prop, level, exind);
305 print_current_node(pgspParserContext *ctx)
307 node_vals *v = ctx->nodevals;
308 StringInfo s = ctx->dest;
310 int level = ctx->level - 1;
315 * The element objects in "Workers" list doesn't have node type, which
316 * would be named T_Worker if there were in node.h. So it needs a special
320 if (v->node_type == T_Invalid && !HASSTRING(v->worker_number))
324 appendStringInfoString(s, "\n");
325 appendStringInfoSpaces(s, TEXT_INDENT_BASE(level, exind));
327 if (HASSTRING(v->subplan_name))
329 appendStringInfoString(s, v->subplan_name);
330 appendStringInfoString(s, "\n");
332 appendStringInfoSpaces(s, TEXT_INDENT_BASE(level, exind));
335 /* list items doesn't need this header */
336 if (level > 1 && ctx->current_list == P_Invalid)
337 appendStringInfoString(s, "-> ");
339 if (v->parallel_aware)
340 appendStringInfoString(s, "Parallel ");
346 case T_BitmapHeapScan:
352 case T_WorkTableScan:
354 if (v->nodetag == T_ModifyTable)
355 appendStringInfoString(s, v->operation);
357 appendStringInfoString(s, v->node_type);
363 case T_IndexOnlyScan:
364 case T_BitmapIndexScan:
365 appendStringInfoString(s, v->node_type);
366 print_prop_if_exists(s, " ", v->scan_dir, 0, 0);
367 print_prop_if_exists(s, " using ", v->index_name, 0, 0);
374 appendStringInfoString(s, v->node_type);
375 if (v->join_type && strcmp(v->join_type, "Inner") != 0)
377 appendStringInfoChar(s, ' ');
378 appendStringInfoString(s, v->join_type);
380 if (v->nodetag != T_NestLoop)
381 appendStringInfoString(s, " Join");
385 appendStringInfoString(s, v->node_type);
386 print_prop_if_exists(s, " ", v->setopcommand, 0, 0);
390 /* Existence of worker_number suggests this is a Worker node */
391 if (HASSTRING(v->worker_number))
393 appendStringInfoString(s, "Worker");
394 print_prop_if_exists(s, " ", v->worker_number, 0, 0);
397 * "Worker"s are individual JSON objects in a JSON list but
398 * should be printed as just a property in text
399 * representaion. Correct indent using exind here.
404 appendStringInfoString(s, v->node_type);
408 /* Don't show costs for child talbes */
409 if (ctx->current_list == P_TargetTables)
412 if (!ISZERO(v->startup_cost) &&
413 !ISZERO(v->total_cost) &&
414 HASSTRING(v->plan_rows) &&
415 HASSTRING(v->plan_width))
417 appendStringInfoString(s, " (cost=");
418 appendStringInfoString(s, v->startup_cost);
419 appendStringInfoString(s, "..");
420 appendStringInfoString(s, v->total_cost);
421 appendStringInfoString(s, " rows=");
422 appendStringInfoString(s, v->plan_rows);
423 appendStringInfoString(s, " width=");
424 appendStringInfoString(s, v->plan_width);
425 appendStringInfoString(s, ")");
428 if (HASSTRING(v->actual_loops) && ISZERO(v->actual_loops))
429 appendStringInfoString(s, " (never executed)");
430 else if (HASSTRING(v->actual_rows) &&
431 HASSTRING(v->actual_loops) &&
432 HASSTRING(v->actual_startup_time) &&
433 HASSTRING(v->actual_total_time))
435 appendStringInfoString(s, " (actual ");
436 appendStringInfoString(s, "time=");
437 appendStringInfoString(s, v->actual_startup_time);
438 appendStringInfoString(s, "..");
439 appendStringInfoString(s, v->actual_total_time);
440 appendStringInfoString(s, " ");
442 appendStringInfoString(s, "rows=");
443 appendStringInfoString(s, v->actual_rows);
445 appendStringInfoString(s, " loops=");
446 appendStringInfoString(s, v->actual_loops);
448 appendStringInfoString(s, ")");
451 foreach(lc, v->target_tables)
453 char *str = (char *)lfirst (lc);
455 appendStringInfoString(s, "\n");
456 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
457 appendStringInfoString(s, str);
460 print_propstr_if_exists(s, "Output: ", v->output, level, exind);
461 print_propstr_if_exists(s, "Group Key: ", v->group_key, level, exind);
462 print_groupingsets_if_exists(s, v->grouping_sets, level, exind);
463 print_prop_if_exists(s, "Merge Cond: ", v->merge_cond, level, exind);
464 print_prop_if_exists(s, "Hash Cond: " , v->hash_cond, level, exind);
465 print_prop_if_exists(s, "Tid Cond: " , v->tid_cond, level, exind);
466 print_prop_if_exists(s, "Join Filter: " , v->join_filter, level, exind);
467 print_prop_if_exists(s, "Index Cond: " , v->index_cond, level, exind);
468 print_prop_if_exists(s, "Recheck Cond: ", v->recheck_cond, level, exind);
469 print_prop_if_exists(s, "Workers Planned: ", v->workers_planned, level, exind);
470 print_prop_if_exists(s, "Workers Launched: ", v->workers_launched, level, exind);
472 if (HASSTRING(v->sampling_method))
474 appendStringInfoString(s, "\n");
475 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
476 appendStringInfo(s, "Sampling: %s (%s)",
478 v->sampling_params ? v->sampling_params->data : "");
479 if (v->repeatable_seed)
480 appendStringInfo(s, " REPEATABLE (%s)", v->repeatable_seed);
483 print_propstr_if_exists(s, "Sort Key: ", v->sort_key, level, exind);
484 if (HASSTRING(v->sort_method))
486 appendStringInfoString(s, "\n");
487 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
488 appendStringInfoString(s, "Sort Method: ");
489 appendStringInfoString(s, v->sort_method);
491 if (HASSTRING(v->sort_space_type) &&
492 HASSTRING(v->sort_space_used))
494 appendStringInfoString(s, " ");
495 appendStringInfoString(s, v->sort_space_type);
496 appendStringInfoString(s, ": ");
497 appendStringInfoString(s, v->sort_space_used);
498 appendStringInfoString(s, "kB");
502 print_prop_if_exists(s, "Function Call: ", v->func_call, level, exind);
505 * Emit unknown properties here. The properties are printed in the same
506 * shape with JSON properties as assumed by explain.c.
508 foreach (lc, v->_undef)
510 StringInfo str = (StringInfo) lfirst(lc);
512 appendStringInfoString(s, "\n");
513 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
514 appendStringInfoString(s, str->data);
518 print_prop_if_exists(s, "Filter: ", v->filter, level, exind);
519 print_prop_if_nz(s, "Rows Removed by Filter: ",
520 v->filter_removed, level, exind);
521 print_prop_if_nz(s, "Rows Removed by Index Recheck: ",
522 v->idxrchk_removed, level, exind);
523 print_prop_if_nz(s, "Rows Removed by Join Filter: ",
524 v->joinfilt_removed, level, exind);
526 if (HASSTRING(v->exact_heap_blks) ||
527 HASSTRING(v->lossy_heap_blks))
529 appendStringInfoString(s, "\n");
530 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
531 appendStringInfoString(s, "Heap Blocks:");
532 print_prop_if_nz(s, " exact=", v->exact_heap_blks, 0, exind);
533 print_prop_if_nz(s, " lossy=", v->lossy_heap_blks, 0, exind);
536 if (!ISZERO(v->hash_buckets))
538 bool show_original = false;
540 appendStringInfoString(s, "\n");
541 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
542 appendStringInfoString(s, "Buckets: ");
543 appendStringInfoString(s, v->hash_buckets);
545 /* See show_hash_info() in explain.c for details */
546 if ((v->org_hash_buckets &&
547 strcmp(v->hash_buckets, v->org_hash_buckets) != 0) ||
548 (v->org_hash_batches &&
549 strcmp(v->hash_batches, v->org_hash_batches) != 0))
550 show_original = true;
552 if (show_original && v->org_hash_buckets)
554 appendStringInfoString(s, " (originally ");
555 appendStringInfoString(s, v->org_hash_buckets);
556 appendStringInfoChar(s, ')');
559 if (!ISZERO(v->hash_batches))
561 appendStringInfoString(s, " Batches: ");
562 appendStringInfoString(s, v->hash_batches);
563 if (show_original && v->org_hash_batches)
565 appendStringInfoString(s, " (originally ");
566 appendStringInfoString(s, v->org_hash_batches);
567 appendStringInfoChar(s, ')');
570 if (!ISZERO(v->peak_memory_usage))
572 appendStringInfoString(s, " Memory Usage: ");
573 appendStringInfoString(s, v->peak_memory_usage);
574 appendStringInfoString(s, "kB");
578 print_prop_if_exists(s, "Heap Fetches: ", v->heap_fetches, level, exind);
579 print_prop_if_exists(s, "Conflict Resolution: ",
580 v->conflict_resolution, level, exind);
581 print_propstr_if_exists(s, "Conflict Arbiter Indexes: ",
582 v->conflict_arbiter_indexes, level, exind);
583 print_prop_if_exists(s, "Tuples Inserted: ",
584 v->tuples_inserted, level, exind);
585 print_prop_if_exists(s, "Conflicting Tuples: ",
586 v->conflicting_tuples, level, exind);
588 if (!ISZERO(v->shared_hit_blks) ||
589 !ISZERO(v->shared_read_blks) ||
590 !ISZERO(v->shared_dirtied_blks) ||
591 !ISZERO(v->shared_written_blks))
593 appendStringInfoString(s, "\n");
594 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
595 appendStringInfoString(s, "Buffers: shared");
597 if (!ISZERO(v->shared_hit_blks))
599 appendStringInfoString(s, " hit=");
600 appendStringInfoString(s, v->shared_hit_blks);
603 if (!ISZERO(v->shared_read_blks))
605 appendStringInfoString(s, " read=");
606 appendStringInfoString(s, v->shared_read_blks);
609 if (!ISZERO(v->shared_dirtied_blks))
611 appendStringInfoString(s, " dirtied=");
612 appendStringInfoString(s, v->shared_dirtied_blks);
615 if (!ISZERO(v->shared_written_blks))
617 appendStringInfoString(s, " written=");
618 appendStringInfoString(s, v->shared_written_blks);
622 if (!ISZERO(v->local_hit_blks) ||
623 !ISZERO(v->local_read_blks) ||
624 !ISZERO(v->local_dirtied_blks) ||
625 !ISZERO(v->local_written_blks))
628 appendStringInfoString(s, ", ");
631 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
632 appendStringInfoString(s, "Buffers: ");
635 appendStringInfoString(s, "local");
636 if (!ISZERO(v->local_hit_blks))
638 appendStringInfoString(s, " hit=");
639 appendStringInfoString(s, v->local_hit_blks);
642 if (!ISZERO(v->local_read_blks))
644 appendStringInfoString(s, " read=");
645 appendStringInfoString(s, v->local_read_blks);
648 if (!ISZERO(v->local_dirtied_blks))
650 appendStringInfoString(s, " dirtied=");
651 appendStringInfoString(s, v->local_dirtied_blks);
654 if (!ISZERO(v->local_written_blks))
656 appendStringInfoString(s, " written=");
657 appendStringInfoString(s, v->local_written_blks);
661 if (!ISZERO(v->temp_read_blks) ||
662 !ISZERO(v->temp_written_blks))
665 appendStringInfoString(s, ", ");
668 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
669 appendStringInfoString(s, "Buffers: ");
672 appendStringInfoString(s, "temp");
673 if (!ISZERO(v->temp_read_blks))
675 appendStringInfoString(s, " read=");
676 appendStringInfoString(s, v->temp_read_blks);
679 if (!ISZERO(v->temp_written_blks))
681 appendStringInfoString(s, " written=");
682 appendStringInfoString(s, v->temp_written_blks);
686 if (!ISZERO(v->io_read_time) ||
687 !ISZERO(v->io_write_time))
689 /* Feed a line if any of Buffers: items has been shown */
691 appendStringInfoString(s, "\n");
693 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
694 appendStringInfoString(s, "I/O Timings: ");
696 if (!ISZERO(v->io_read_time))
698 appendStringInfoString(s, " read=");
699 appendStringInfoString(s, v->io_read_time);
701 if (!ISZERO(v->io_write_time))
703 appendStringInfoString(s, " write=");
704 appendStringInfoString(s, v->io_write_time);
710 print_current_trig_node(pgspParserContext *ctx)
712 node_vals *v = ctx->nodevals;
713 StringInfo s = ctx->dest;
715 if (HASSTRING(v->trig_name) && !ISZERO(v->trig_time))
718 appendStringInfoString(s, "\n");
719 appendStringInfoString(s, "Trigger ");
720 appendStringInfoString(s, v->trig_name);
721 appendStringInfoString(s, ": time=");
722 appendStringInfoString(s, v->trig_time);
723 appendStringInfoString(s, " calls=");
724 appendStringInfoString(s, v->trig_calls);
730 clear_nodeval(node_vals *vals)
732 memset(vals, 0, sizeof(node_vals));
736 json_text_objstart(void *state)
738 pgspParserContext *ctx = (pgspParserContext *)state;
741 /* Create new grouping sets or reset existing ones */
742 if (ctx->current_list == P_GroupSets)
744 node_vals *v = ctx->nodevals;
746 ctx->tmp_gset = (grouping_set*) palloc0(sizeof(grouping_set));
748 v->sort_key = makeStringInfo();
750 v->group_key = makeStringInfo();
752 v->hash_key = makeStringInfo();
753 resetStringInfo(v->sort_key);
754 resetStringInfo(v->group_key);
755 resetStringInfo(v->hash_key);
760 json_text_objend(void *state)
762 pgspParserContext *ctx = (pgspParserContext *)state;
764 /* Print current node if the object is a P_Plan or a child of P_Plans */
765 if (bms_is_member(ctx->level - 1, ctx->plan_levels))
767 print_current_node(ctx);
768 clear_nodeval(ctx->nodevals);
770 else if (ctx->section == P_Triggers)
772 print_current_trig_node(ctx);
773 clear_nodeval(ctx->nodevals);
775 else if (ctx->current_list == P_TargetTables)
777 /* Move the current working taget tables into nodevals */
778 node_vals *v = ctx->nodevals;
781 ctx->work_str = makeStringInfo();
783 resetStringInfo(ctx->work_str);
784 appendStringInfoString(ctx->work_str, v->operation);
785 print_obj_name0(ctx->work_str, v->obj_name, v->schema_name, v->alias);
786 v->target_tables = lappend(v->target_tables,
787 pstrdup(ctx->work_str->data));
788 resetStringInfo(ctx->work_str);
790 else if (ctx->current_list == P_GroupSets && ctx->tmp_gset)
792 /* Move working grouping set into nodevals */
793 node_vals *v = ctx->nodevals;
795 /* Copy sort key if any */
796 if (v->sort_key->data[0])
798 ctx->tmp_gset->sort_keys = strdup(v->sort_key->data);
799 resetStringInfo(v->sort_key);
802 /* Move working grouping set into nodevals */
803 ctx->nodevals->grouping_sets =
804 lappend(v->grouping_sets, ctx->tmp_gset);
805 ctx->tmp_gset = NULL;
808 ctx->last_elem_is_object = true;
813 json_text_arrstart(void *state)
815 pgspParserContext *ctx = (pgspParserContext *)state;
817 if (ctx->current_list == P_GroupSets)
824 json_text_arrend(void *state)
826 pgspParserContext *ctx = (pgspParserContext *)state;
828 if (ctx->current_list == P_GroupSets)
831 * wlist_level means that now at the end of innermost list of Group
834 if (ctx->wlist_level == 3)
836 node_vals *v = ctx->nodevals;
839 * At this point, v->group_key holds the keys in "Group Keys". The
840 * item holds a double-nested list and the innermost lists are to
841 * go into individual "Group Key" lines. Empty innermost list is
842 * represented as "()" there. See explain.c of PostgreSQL.
844 ctx->tmp_gset->key_type = "Group Key: ";
845 if (v->group_key->data[0])
847 ctx->tmp_gset->group_keys =
848 lappend(ctx->tmp_gset->group_keys,
849 pstrdup(v->group_key->data));
851 else if (v->hash_key->data[0])
853 ctx->tmp_gset->group_keys =
854 lappend(ctx->tmp_gset->group_keys,
855 pstrdup(v->hash_key->data));
856 ctx->tmp_gset->key_type = "Hash Key: ";
859 ctx->tmp_gset->group_keys =
860 lappend(ctx->tmp_gset->group_keys, "()");
862 resetStringInfo(ctx->nodevals->group_key);
863 resetStringInfo(ctx->nodevals->hash_key);
870 json_text_ofstart(void *state, char *fname, bool isnull)
873 pgspParserContext *ctx = (pgspParserContext *)state;
876 p = search_word_table(propfields, fname, PGSP_JSON_TEXTIZE);
881 (errmsg("Short JSON parser encoutered unknown field name: \"%s\", skipped.", fname),
882 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
885 * Unknown properties may be put by foreign data wrappers and assumed
886 * to be printed in the same format to JSON properties. We store in
887 * nodevals a string emittable as-is in text explains.
889 ctx->setter = SETTER(_undef);
890 ctx->nodevals->_undef_newelem = true;
891 ctx->setter(ctx->nodevals, fname);
892 ctx->nodevals->_undef_newelem = false;
893 ctx->setter(ctx->nodevals, ": ");
898 * Print the current node immediately if the next level of
899 * Plan/Plans/Worers comes. This assumes that the plan output is
900 * strcutured tail-recursively.
902 if (p->tag == P_Plan || p->tag == P_Plans || p->tag == P_Workers)
904 print_current_node(ctx);
905 clear_nodeval(ctx->nodevals);
907 else if (p->tag == P_TargetTables)
909 node_vals *v = ctx->nodevals;
911 ctx->current_list = p->tag;
912 ctx->list_fname = fname;
914 /* stash some data */
915 v->tmp_obj_name = v->obj_name;
916 v->tmp_schema_name = v->schema_name;
917 v->tmp_alias = v->alias;
920 if (p->tag == P_GroupSets || p->tag == P_Workers)
922 ctx->current_list = p->tag;
923 ctx->list_fname = fname;
924 ctx->wlist_level = 0;
928 * This paser prints partial result at the end of every P_Plan object,
929 * which includes elements in P_Plans list.
931 if (p->tag == P_Plan || p->tag == P_Plans || p->tag == P_Workers)
932 ctx->plan_levels = bms_add_member(ctx->plan_levels, ctx->level);
934 ctx->plan_levels = bms_del_member(ctx->plan_levels, ctx->level);
936 if (p->tag == P_Plan || p->tag == P_Triggers)
937 ctx->section = p->tag;
938 ctx->setter = p->setter;
943 json_text_ofend(void *state, char *fname, bool isnull)
945 pgspParserContext *ctx = (pgspParserContext *)state;
946 node_vals *v = ctx->nodevals;
948 /* We assume that lists with same fname will not be nested */
949 if (ctx->list_fname && strcmp(fname, ctx->list_fname) == 0)
951 /* Restore stashed data, see json_text_ofstart */
952 if (ctx->current_list == P_TargetTables)
954 v->obj_name = v->tmp_obj_name;
955 v->schema_name = v->tmp_schema_name;
956 v->alias = v->tmp_alias;
959 ctx->list_fname = NULL;
960 ctx->current_list = P_Invalid;
963 /* Planning/Execution time to be appeared at the end of plan */
964 if (HASSTRING(v->plan_time) ||
965 HASSTRING(v->exec_time))
967 if (HASSTRING(v->plan_time))
969 appendStringInfoString(ctx->dest, "\nPlanning Time: ");
970 appendStringInfoString(ctx->dest, v->plan_time);
971 appendStringInfoString(ctx->dest, " ms");
975 appendStringInfoString(ctx->dest, "\nExecution Time: ");
976 appendStringInfoString(ctx->dest, v->exec_time);
977 appendStringInfoString(ctx->dest, " ms");
984 json_text_scalar(void *state, char *token, JsonTokenType tokentype)
986 pgspParserContext *ctx = (pgspParserContext *)state;
989 ctx->setter(ctx->nodevals, token);
993 pgsp_json_textize(char *json)
997 pgspParserContext ctx;
999 init_json_lex_context(&lex, json);
1000 init_parser_context(&ctx, PGSP_JSON_TEXTIZE, json, NULL, 0);
1002 ctx.nodevals = (node_vals*)palloc0(sizeof(node_vals));
1004 sem.semstate = (void*)&ctx;
1005 sem.object_start = json_text_objstart;
1006 sem.object_end = json_text_objend;
1007 sem.array_start = json_text_arrstart;
1008 sem.array_end = json_text_arrend;
1009 sem.object_field_start = json_text_ofstart;
1010 sem.object_field_end = json_text_ofend;
1011 sem.array_element_start= NULL;
1012 sem.array_element_end = NULL;
1013 sem.scalar = json_text_scalar;
1016 if (!run_pg_parse_json(&lex, &sem))
1018 if (ctx.nodevals->node_type)
1019 print_current_node(&ctx);
1021 if (ctx.dest->len > 0 &&
1022 ctx.dest->data[ctx.dest->len - 1] != '\n')
1023 appendStringInfoChar(ctx.dest, '\n');
1025 if (ctx.dest->len == 0)
1026 appendStringInfoString(ctx.dest, "<Input was not JSON>");
1028 appendStringInfoString(ctx.dest, "<truncated>");
1031 pfree(ctx.nodevals);
1033 return ctx.dest->data;