3 #include "nodes/nodes.h"
4 #include "nodes/bitmapset.h"
5 #include "utils/json.h"
6 #include "utils/jsonapi.h"
7 #include "utils/builtins.h"
9 #include "pgsp_json_text.h"
10 #include "pgsp_json_int.h"
12 static void clear_nodeval(node_vals *vals);
13 static void print_current_node(pgspParserContext *ctx);
14 static void print_current_trig_node(pgspParserContext *ctx);
15 static void print_prop(StringInfo s, char *prepstr,
16 const char *prop, int leve, int exind);
17 static void print_prop_if_exists(StringInfo s, char *prepstr,
18 const char *prop, int leve, int exind);
19 static void print_prop_if_nz(StringInfo s, char *prepstr,
20 const char *prop, int leve, int exind);
21 static void json_text_objstart(void *state);
22 static void json_text_objend(void *state);
23 static void json_text_ofstart(void *state, char *fname, bool isnull);
24 static void json_text_ofend(void *state, char *fname, bool isnull);
25 static void json_text_scalar(void *state, char *token, JsonTokenType tokentype);
27 /* Parser callbacks for plan textization */
32 vals->node_type = val;
33 vals->nodetag = T_Invalid;
35 p = search_word_table(nodetypes, val, PGSP_JSON_TEXTIZE);
38 vals->node_type = (p->textname ? p->textname : p->longname);
39 vals->nodetag = p->tag;
47 vals->output = makeStringInfo();
48 appendStringInfoString(vals->output, val);
52 appendStringInfoString(vals->output, ", ");
53 appendStringInfoString(vals->output, val);
60 p = search_word_table(strategies, val, PGSP_JSON_TEXTIZE);
62 switch (vals->nodetag)
68 vals->node_type = "HashAggregate"; break;
70 vals->node_type = "GroupAggregate"; break;
77 if (p->tag == S_Hashed)
78 vals->node_type = "HashSetOp";
85 CONVERSION_SETTER(scan_dir, conv_scandir);
86 SQLQUOTE_SETTER(obj_name);
87 SQLQUOTE_SETTER(alias);
88 SQLQUOTE_SETTER(schema_name);
89 DEFAULT_SETTER(merge_cond);
90 CONVERSION_SETTER(join_type, conv_jointype);
91 CONVERSION_SETTER(setopcommand, conv_setsetopcommand);
92 CONVERSION_SETTER(sort_method, conv_sortmethod);
93 DEFAULT_SETTER(sort_key);
94 SQLQUOTE_SETTER(index_name);
95 DEFAULT_SETTER(startup_cost);
96 DEFAULT_SETTER(total_cost);
97 DEFAULT_SETTER(plan_rows);
98 DEFAULT_SETTER(plan_width);
99 DEFAULT_SETTER(sort_space_used);
100 CONVERSION_SETTER(sort_space_type, conv_sortspacetype);
101 DEFAULT_SETTER(filter);
102 DEFAULT_SETTER(join_filter);
103 DEFAULT_SETTER(func_call);
104 DEFAULT_SETTER(index_cond);
105 DEFAULT_SETTER(recheck_cond);
106 CONVERSION_SETTER(operation, conv_operation);
107 DEFAULT_SETTER(subplan_name);
108 DEFAULT_SETTER(hash_cond);
109 DEFAULT_SETTER(tid_cond);
110 DEFAULT_SETTER(filter_removed);
111 DEFAULT_SETTER(idxrchk_removed);
112 DEFAULT_SETTER(peak_memory_usage);
113 DEFAULT_SETTER(org_hash_batches);
114 DEFAULT_SETTER(hash_batches);
115 DEFAULT_SETTER(hash_buckets);
116 DEFAULT_SETTER(actual_startup_time);
117 DEFAULT_SETTER(actual_total_time);
118 DEFAULT_SETTER(actual_rows);
119 DEFAULT_SETTER(actual_loops);
120 DEFAULT_SETTER(heap_fetches);
121 DEFAULT_SETTER(shared_hit_blks);
122 DEFAULT_SETTER(shared_read_blks);
123 DEFAULT_SETTER(shared_dirtied_blks);
124 DEFAULT_SETTER(shared_written_blks);
125 DEFAULT_SETTER(local_hit_blks);
126 DEFAULT_SETTER(local_read_blks);
127 DEFAULT_SETTER(local_dirtied_blks);
128 DEFAULT_SETTER(local_written_blks);
129 DEFAULT_SETTER(temp_read_blks);
130 DEFAULT_SETTER(temp_written_blks);
131 DEFAULT_SETTER(io_read_time);
132 DEFAULT_SETTER(io_write_time);
133 SQLQUOTE_SETTER(trig_name);
134 SQLQUOTE_SETTER(trig_relation);
135 DEFAULT_SETTER(trig_time);
136 DEFAULT_SETTER(trig_calls);
137 DEFAULT_SETTER(plan_time);
138 DEFAULT_SETTER(exec_time);
139 DEFAULT_SETTER(exact_heap_blks);
140 DEFAULT_SETTER(lossy_heap_blks);
141 DEFAULT_SETTER(joinfilt_removed);
143 #define ISZERO(s) (!s || strcmp(s, "0") == 0 || strcmp(s, "0.000") == 0 )
144 #define HASSTRING(s) (s && strlen(s) > 0)
145 #define TEXT_LEVEL_STEP 6
146 #define TEXT_INDENT_OFFSET 2
147 #define TEXT_INDENT_BASE(l, e) \
148 ((l < 2) ? 0 : (TEXT_LEVEL_STEP * (l - 2) + TEXT_INDENT_OFFSET) + e)
149 #define TEXT_INDENT_DETAILS(l, e) \
150 (TEXT_INDENT_BASE(l, e) + ((l < 2) ? 2 : 6))
153 print_obj_name(pgspParserContext *ctx)
155 node_vals *v = ctx->nodevals;
156 StringInfo s = ctx->dest;
157 bool on_written = false;
159 if (HASSTRING(v->obj_name))
162 appendStringInfoString(s, " on ");
163 if (HASSTRING(v->schema_name))
165 appendStringInfoString(s, v->schema_name);
166 appendStringInfoChar(s, '.');
168 appendStringInfoString(s, v->obj_name);
170 if (HASSTRING(v->alias) &&
171 (!HASSTRING(v->obj_name) || strcmp(v->obj_name, v->alias) != 0))
174 appendStringInfoString(s, " on ");
176 appendStringInfoChar(s, ' ');
177 appendStringInfoString(s, v->alias);
183 print_prop(StringInfo s, char *prepstr,
184 const char *prop, int level, int exind)
188 appendStringInfoString(s, "\n");
189 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
191 appendStringInfoString(s, prepstr);
192 appendStringInfoString(s, prop);
196 print_prop_if_exists(StringInfo s, char *prepstr,
197 const char *prop, int level, int exind)
200 print_prop(s, prepstr, prop, level, exind);
204 print_prop_if_nz(StringInfo s, char *prepstr,
205 const char *prop, int level, int exind)
208 print_prop(s, prepstr, prop, level, exind);
212 print_current_node(pgspParserContext *ctx)
214 node_vals *v = ctx->nodevals;
215 StringInfo s = ctx->dest;
216 int level = ctx->level - 1;
220 if (v->node_type == T_Invalid)
224 appendStringInfoString(s, "\n");
225 appendStringInfoSpaces(s, TEXT_INDENT_BASE(level, exind));
227 if (HASSTRING(v->subplan_name))
229 appendStringInfoString(s, v->subplan_name);
230 appendStringInfoString(s, "\n");
232 appendStringInfoSpaces(s, TEXT_INDENT_BASE(level, exind));
236 appendStringInfoString(s, "-> ");
242 case T_BitmapHeapScan:
248 case T_WorkTableScan:
250 if (v->nodetag == T_ModifyTable)
251 appendStringInfoString(s, v->operation);
253 appendStringInfoString(s, v->node_type);
259 case T_IndexOnlyScan:
260 case T_BitmapIndexScan:
261 appendStringInfoString(s, v->node_type);
262 print_prop_if_exists(s, " ", v->scan_dir, 0, 0);
263 print_prop_if_exists(s, " using ", v->index_name, 0, 0);
270 appendStringInfoString(s, v->node_type);
271 if (v->join_type && strcmp(v->join_type, "Inner") != 0)
273 appendStringInfoChar(s, ' ');
274 appendStringInfoString(s, v->join_type);
276 if (v->nodetag != T_NestLoop)
277 appendStringInfoString(s, " Join");
281 appendStringInfoString(s, v->node_type);
282 print_prop_if_exists(s, " ", v->setopcommand, 0, 0);
286 appendStringInfoString(s, v->node_type);
290 if (!ISZERO(v->startup_cost) &&
291 !ISZERO(v->total_cost) &&
292 HASSTRING(v->plan_rows) &&
293 HASSTRING(v->plan_width))
295 appendStringInfoString(s, " (cost=");
296 appendStringInfoString(s, v->startup_cost);
297 appendStringInfoString(s, "..");
298 appendStringInfoString(s, v->total_cost);
299 appendStringInfoString(s, " rows=");
300 appendStringInfoString(s, v->plan_rows);
301 appendStringInfoString(s, " width=");
302 appendStringInfoString(s, v->plan_width);
303 appendStringInfoString(s, ")");
306 if (HASSTRING(v->actual_loops) && ISZERO(v->actual_loops))
307 appendStringInfoString(s, " (never executed)");
308 else if (HASSTRING(v->actual_rows) &&
309 HASSTRING(v->actual_loops) &&
310 HASSTRING(v->actual_startup_time) &&
311 HASSTRING(v->actual_total_time))
313 appendStringInfoString(s, " (actual ");
314 appendStringInfoString(s, "time=");
315 appendStringInfoString(s, v->actual_startup_time);
316 appendStringInfoString(s, "..");
317 appendStringInfoString(s, v->actual_total_time);
318 appendStringInfoString(s, " ");
320 appendStringInfoString(s, "rows=");
321 appendStringInfoString(s, v->actual_rows);
323 appendStringInfoString(s, " loops=");
324 appendStringInfoString(s, v->actual_loops);
326 appendStringInfoString(s, ")");
331 appendStringInfoString(s, "\n");
332 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
333 appendStringInfoString(s, "Output: ");
334 appendStringInfoString(s, v->output->data);
336 print_prop_if_exists(s, "Merge Cond: ", v->merge_cond, level, exind);
337 print_prop_if_exists(s, "Hash Cond: " , v->hash_cond, level, exind);
338 print_prop_if_exists(s, "Tid Cond: " , v->tid_cond, level, exind);
339 print_prop_if_exists(s, "Join Filter: " , v->join_filter, level, exind);
340 print_prop_if_exists(s, "Index Cond: " , v->index_cond, level, exind);
341 print_prop_if_exists(s, "Recheck Cond: ", v->recheck_cond, level, exind);
342 print_prop_if_exists(s, "Sort Key: ", v->sort_key, level, exind);
344 if (HASSTRING(v->sort_method))
346 appendStringInfoString(s, "\n");
347 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
348 appendStringInfoString(s, "Sort Method: ");
349 appendStringInfoString(s, v->sort_method);
351 if (HASSTRING(v->sort_space_type) &&
352 HASSTRING(v->sort_space_used))
354 appendStringInfoString(s, " ");
355 appendStringInfoString(s, v->sort_space_type);
356 appendStringInfoString(s, ": ");
357 appendStringInfoString(s, v->sort_space_used);
358 appendStringInfoString(s, "kB");
362 print_prop_if_exists(s, "Function Call: ", v->func_call, level, exind);
363 print_prop_if_exists(s, "Filter: ", v->filter, level, exind);
364 print_prop_if_nz(s, "Rows Removed by Filter: ",
365 v->filter_removed, level, exind);
366 print_prop_if_nz(s, "Rows Removed by Index Recheck: ",
367 v->idxrchk_removed, level, exind);
368 print_prop_if_nz(s, "Rows Removed by Join Filter: ",
369 v->joinfilt_removed, level, exind);
371 if (HASSTRING(v->exact_heap_blks) ||
372 HASSTRING(v->lossy_heap_blks))
374 appendStringInfoString(s, "\n");
375 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
376 appendStringInfoString(s, "Heap Blocks:");
377 print_prop_if_nz(s, " exact=", v->exact_heap_blks, 0, exind);
378 print_prop_if_nz(s, " lossy=", v->lossy_heap_blks, 0, exind);
381 if (!ISZERO(v->hash_buckets))
383 appendStringInfoString(s, "\n");
384 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
385 appendStringInfoString(s, "Buckets: ");
386 appendStringInfoString(s, v->hash_buckets);
387 if (!ISZERO(v->hash_batches))
389 appendStringInfoString(s, " Batches: ");
390 appendStringInfoString(s, v->hash_batches);
391 if (v->org_hash_batches &&
392 strcmp(v->hash_batches, v->org_hash_batches) != 0)
394 appendStringInfoString(s, " (originally ");
395 appendStringInfoString(s, v->org_hash_batches);
396 appendStringInfoChar(s, ')');
399 if (!ISZERO(v->peak_memory_usage))
401 appendStringInfoString(s, " Memory Usage: ");
402 appendStringInfoString(s, v->peak_memory_usage);
403 appendStringInfoString(s, "kB");
407 print_prop_if_exists(s, "Heap Fetches: ", v->heap_fetches, level, exind);
409 if (!ISZERO(v->shared_hit_blks) ||
410 !ISZERO(v->shared_read_blks) ||
411 !ISZERO(v->shared_dirtied_blks) ||
412 !ISZERO(v->shared_written_blks))
414 appendStringInfoString(s, "\n");
415 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
416 appendStringInfoString(s, "Buffers: shared");
418 if (!ISZERO(v->shared_hit_blks))
420 appendStringInfoString(s, " hit=");
421 appendStringInfoString(s, v->shared_hit_blks);
424 if (!ISZERO(v->shared_read_blks))
426 appendStringInfoString(s, " read=");
427 appendStringInfoString(s, v->shared_read_blks);
430 if (!ISZERO(v->shared_dirtied_blks))
432 appendStringInfoString(s, " dirtied=");
433 appendStringInfoString(s, v->shared_dirtied_blks);
436 if (!ISZERO(v->shared_written_blks))
438 appendStringInfoString(s, " written=");
439 appendStringInfoString(s, v->shared_written_blks);
443 if (!ISZERO(v->local_hit_blks) ||
444 !ISZERO(v->local_read_blks) ||
445 !ISZERO(v->local_dirtied_blks) ||
446 !ISZERO(v->local_written_blks))
449 appendStringInfoString(s, ", ");
452 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
453 appendStringInfoString(s, "Buffers: ");
456 appendStringInfoString(s, "local");
457 if (!ISZERO(v->local_hit_blks))
459 appendStringInfoString(s, " hit=");
460 appendStringInfoString(s, v->local_hit_blks);
463 if (!ISZERO(v->local_read_blks))
465 appendStringInfoString(s, " read=");
466 appendStringInfoString(s, v->local_read_blks);
469 if (!ISZERO(v->local_dirtied_blks))
471 appendStringInfoString(s, " dirtied=");
472 appendStringInfoString(s, v->local_dirtied_blks);
475 if (!ISZERO(v->local_written_blks))
477 appendStringInfoString(s, " written=");
478 appendStringInfoString(s, v->local_written_blks);
482 if (!ISZERO(v->temp_read_blks) ||
483 !ISZERO(v->temp_written_blks))
486 appendStringInfoString(s, ", ");
489 appendStringInfoSpaces(s, TEXT_INDENT_DETAILS(level, exind));
490 appendStringInfoString(s, "Buffers: ");
493 appendStringInfoString(s, "temp");
494 if (!ISZERO(v->temp_read_blks))
496 appendStringInfoString(s, " read=");
497 appendStringInfoString(s, v->temp_read_blks);
500 if (!ISZERO(v->temp_written_blks))
502 appendStringInfoString(s, " written=");
503 appendStringInfoString(s, v->temp_written_blks);
510 print_current_trig_node(pgspParserContext *ctx)
512 node_vals *v = ctx->nodevals;
513 StringInfo s = ctx->dest;
515 if (HASSTRING(v->trig_name) && !ISZERO(v->trig_time))
518 appendStringInfoString(s, "\n");
519 appendStringInfoString(s, "Trigger ");
520 appendStringInfoString(s, v->trig_name);
521 appendStringInfoString(s, ": time=");
522 appendStringInfoString(s, v->trig_time);
523 appendStringInfoString(s, " calls=");
524 appendStringInfoString(s, v->trig_calls);
530 clear_nodeval(node_vals *vals)
532 memset(vals, 0, sizeof(node_vals));
536 json_text_objstart(void *state)
538 pgspParserContext *ctx = (pgspParserContext *)state;
539 clear_nodeval(ctx->nodevals);
543 json_text_objend(void *state)
545 pgspParserContext *ctx = (pgspParserContext *)state;
546 switch (ctx->processing)
549 print_current_node(ctx);
552 print_current_trig_node(ctx);
558 clear_nodeval(ctx->nodevals);
559 ctx->last_elem_is_object = true;
564 json_text_ofstart(void *state, char *fname, bool isnull)
567 pgspParserContext *ctx = (pgspParserContext *)state;
570 p = search_word_table(propfields, fname, PGSP_JSON_TEXTIZE);
575 (errmsg("Short JSON parser encoutered unknown field name: \"%s\", skipped.", fname),
576 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
580 /* Print node immediately if next level of Plan/Plans comes */
581 if (p->tag == P_Plan || p->tag == P_Plans)
583 print_current_node(ctx);
584 clear_nodeval(ctx->nodevals);
588 if (p->tag == P_Plan || p->tag == P_Triggers)
589 ctx->processing = p->tag;
590 ctx->setter = p->setter;
595 json_text_ofend(void *state, char *fname, bool isnull)
597 pgspParserContext *ctx = (pgspParserContext *)state;
598 node_vals *v = ctx->nodevals;
600 /* Planning/Execution time to be appeared at the end of plan */
601 if (HASSTRING(v->plan_time) ||
602 HASSTRING(v->exec_time))
604 if (HASSTRING(v->plan_time))
606 appendStringInfoString(ctx->dest, "\nPlanning Time: ");
607 appendStringInfoString(ctx->dest, v->plan_time);
608 appendStringInfoString(ctx->dest, " ms");
612 appendStringInfoString(ctx->dest, "\nExecution Time: ");
613 appendStringInfoString(ctx->dest, v->exec_time);
614 appendStringInfoString(ctx->dest, " ms");
621 json_text_scalar(void *state, char *token, JsonTokenType tokentype)
623 pgspParserContext *ctx = (pgspParserContext *)state;
626 ctx->setter(ctx->nodevals, token);
630 pgsp_json_textize(char *json)
634 pgspParserContext ctx;
636 init_json_lex_context(&lex, json);
637 init_parser_context(&ctx, PGSP_JSON_TEXTIZE, json, NULL, 0);
639 ctx.nodevals = (node_vals*)palloc0(sizeof(node_vals));
641 sem.semstate = (void*)&ctx;
642 sem.object_start = json_text_objstart;
643 sem.object_end = json_text_objend;
644 sem.array_start = NULL;
645 sem.array_end = NULL;
646 sem.object_field_start = json_text_ofstart;
647 sem.object_field_end = json_text_ofend;
648 sem.array_element_start= NULL;
649 sem.array_element_end = NULL;
650 sem.scalar = json_text_scalar;
653 if (!run_pg_parse_json(&lex, &sem))
655 if (ctx.nodevals->node_type)
656 print_current_node(&ctx);
658 if (ctx.dest->len > 0 &&
659 ctx.dest->data[ctx.dest->len - 1] != '\n')
660 appendStringInfoChar(ctx.dest, '\n');
662 if (ctx.dest->len == 0)
663 appendStringInfoString(ctx.dest, "<Input was not JSON>");
665 appendStringInfoString(ctx.dest, "<truncated>");
670 return ctx.dest->data;