3 #include "nodes/nodes.h"
4 #include "nodes/parsenodes.h"
5 #include "nodes/bitmapset.h"
6 #include "parser/scanner.h"
7 #include "parser/gram.h"
9 #include "utils/json.h"
10 #include "utils/jsonapi.h"
12 #include "pgsp_json.h"
13 #include "pgsp_json_int.h"
18 void normalize_expr(char *expr, bool preserve_space);
19 static const char *coverter_core(word_table *tbl,
20 const char *src, pgsp_parser_mode mode);
22 static void json_objstart(void *state);
23 static void json_objend(void *state);
24 static void json_arrstart(void *state);
25 static void json_arrend(void *state);
26 static void json_ofstart(void *state, char *fname, bool isnull);
27 static void json_aestart(void *state, bool isnull);
28 static void json_scalar(void *state, char *token, JsonTokenType tokentype);
30 static void yaml_objstart(void *state);
31 static void yaml_objend(void *state);
32 static void yaml_arrstart(void *state);
33 static void yaml_arrend(void *state);
34 static void yaml_ofstart(void *state, char *fname, bool isnull);
35 static void yaml_aestart(void *state, bool isnull);
36 static void yaml_scalar(void *state, char *token, JsonTokenType tokentype);
38 static void adjust_wbuf(pgspParserContext *ctx, int len);
39 static char *hyphenate_words(pgspParserContext *ctx, char *src);
40 static void xml_objstart(void *state);
41 static void xml_objend(void *state);
42 static void xml_arrend(void *state);
43 static void xml_ofstart(void *state, char *fname, bool isnull);
44 static void xml_ofend(void *state, char *fname, bool isnull);
45 static void xml_aestart(void *state, bool isnull);
46 static void xml_aeend(void *state, bool isnull);
47 static void xml_scalar(void *state, char *token, JsonTokenType tokentype) ;
49 static void init_json_semaction(JsonSemAction *sem,
50 pgspParserContext *ctx);
52 word_table propfields[] =
54 {P_NodeType, "t" ,"Node Type", NULL, true, conv_nodetype, SETTER(node_type)},
55 {P_RelationShip, "h" ,"Parent Relationship", NULL, true, conv_relasionship, NULL},
56 {P_RelationName, "n" ,"Relation Name", NULL, true, NULL, SETTER(obj_name)},
57 {P_FunctioName, "f" ,"Function Name", NULL, true, NULL, SETTER(obj_name)},
58 {P_IndexName, "i" ,"Index Name", NULL, true, NULL, SETTER(index_name)},
59 {P_CTEName, "c" ,"CTE Name", NULL, true, NULL, SETTER(obj_name)},
60 {P_TrgRelation, "w" ,"Relation", NULL, true, NULL, SETTER(trig_relation)},
61 {P_Schema, "s" ,"Schema", NULL, true, NULL, SETTER(schema_name)},
62 {P_Alias, "a" ,"Alias", NULL, true, NULL, SETTER(alias)},
63 {P_Output, "o" ,"Output", NULL, true, conv_expression, SETTER(output)},
64 {P_ScanDir, "d" ,"Scan Direction", NULL, true, conv_scandir, SETTER(scan_dir)},
65 {P_MergeCond, "m" ,"Merge Cond", NULL, true, conv_expression, SETTER(merge_cond)},
66 {P_Strategy, "g" ,"Strategy", NULL, true, conv_strategy, SETTER(strategy)},
67 {P_JoinType, "j" ,"Join Type", NULL, true, conv_jointype, SETTER(join_type)},
68 {P_SortMethod, "e" ,"Sort Method", NULL, true, conv_sortmethod, SETTER(sort_method)},
69 {P_SortKey, "k" ,"Sort Key", NULL, true, NULL, SETTER(sort_key)},
70 {P_Filter, "5" ,"Filter", NULL, true, conv_expression, SETTER(filter)},
71 {P_JoinFilter, "6" ,"Join Filter", NULL, true, conv_expression, SETTER(join_filter)},
72 {P_HashCond, "7" ,"Hash Cond", NULL, true, conv_expression, SETTER(hash_cond)},
73 {P_IndexCond, "8" ,"Index Cond", NULL, true, conv_expression, SETTER(index_cond)},
74 {P_TidCond, "9" ,"TID Cond", NULL, true, conv_expression, SETTER(tid_cond)},
75 {P_RecheckCond, "0" ,"Recheck Cond", NULL, true, conv_expression, SETTER(recheck_cond)},
76 {P_Operation, "!" ,"Operation", NULL, true, conv_operation, SETTER(operation)},
77 {P_SubplanName, "q" ,"Subplan Name", NULL, true, NULL, SETTER(subplan_name)},
78 {P_Command, "b" ,"Command", NULL, true, conv_setsetopcommand,SETTER(setopcommand)},
79 {P_Triggers, "r" ,"Triggers", NULL, true, NULL, NULL},
80 {P_Trigger, "u" ,"Trigger", NULL, true, NULL, SETTER(node_type)},
81 {P_TriggerName, "v" ,"Trigger Name", NULL, true, NULL, SETTER(trig_name)},
82 {P_ConstraintName, "x" ,"Constraint Name", NULL, true, NULL, NULL},
83 {P_Plans, "l" ,"Plans", NULL, true, NULL, NULL},
84 {P_Plan, "p" ,"Plan", NULL, true, NULL, NULL},
86 /* Values of these properties are masked on normalization */
87 {P_FunctionCall, "y" ,"Function Call", NULL, false, NULL, SETTER(func_call)},
88 {P_StartupCost, "1" ,"Startup Cost", NULL, false, NULL, SETTER(startup_cost)},
89 {P_TotalCost, "2" ,"Total Cost", NULL, false, NULL, SETTER(total_cost)},
90 {P_PlanRows, "3" ,"Plan Rows", NULL, false, NULL, SETTER(plan_rows)},
91 {P_PlanWidth, "4" ,"Plan Width", NULL, false, NULL, SETTER(plan_width)},
92 {P_ActualStartupTime,"A","Actual Startup Time", NULL, false, NULL, SETTER(actual_startup_time)},
93 {P_ActualTotalTime, "B" ,"Actual Total Time", NULL, false, NULL, SETTER(actual_total_time)},
94 {P_ActualRows, "C" ,"Actual Rows", NULL, false, NULL, SETTER(actual_rows)},
95 {P_ActualLoops, "D" ,"Actual Loops", NULL, false, NULL, SETTER(actual_loops)},
96 {P_HeapFetches, "E" ,"Heap Fetches", NULL, false, NULL, SETTER(heap_fetches)},
97 {P_SharedHitBlks, "F" ,"Shared Hit Blocks", NULL, false, NULL, SETTER(shared_hit_blks)},
98 {P_SharedReadBlks, "G" ,"Shared Read Blocks", NULL, false, NULL, SETTER(shared_read_blks)},
99 {P_SharedDirtiedBlks,"H","Shared Dirtied Blocks",NULL,false, NULL, SETTER(shared_dirtied_blks)},
100 {P_SharedWrittenBlks,"I","Shared Written Blocks",NULL,false, NULL, SETTER(shared_written_blks)},
101 {P_LocalHitBlks, "J" ,"Local Hit Blocks", NULL, false, NULL, SETTER(local_hit_blks)},
102 {P_LocalReadBlks, "K" ,"Local Read Blocks", NULL, false, NULL, SETTER(local_read_blks)},
103 {P_LocalDirtiedBlks,"L" ,"Local Dirtied Blocks",NULL, false, NULL, SETTER(local_dirtied_blks)},
104 {P_LocalWrittenBlks,"M" ,"Local Written Blocks",NULL, false, NULL, SETTER(local_written_blks)},
105 {P_TempReadBlks, "N" ,"Temp Read Blocks", NULL, false, NULL, SETTER(temp_read_blks)},
106 {P_TempWrittenBlks, "O" ,"Temp Written Blocks", NULL, false, NULL, SETTER(temp_written_blks)},
107 {P_IOReadTime, "P" ,"I/O Read Time", NULL, false, NULL, SETTER(io_read_time)},
108 {P_IOWwriteTime, "Q" ,"I/O Write Time", NULL, false, NULL, SETTER(io_write_time)},
109 {P_SortSpaceUsed, "R" ,"Sort Space Used", NULL, false, NULL, SETTER(sort_space_used)},
110 {P_SortSpaceType, "S" ,"Sort Space Type", NULL, false, conv_sortspacetype,SETTER(sort_space_type)},
111 {P_PeakMemoryUsage, "T" ,"Peak Memory Usage", NULL, false, NULL, SETTER(peak_memory_usage)},
112 {P_OrgHashBatches, "U","Original Hash Batches",NULL, false, NULL, SETTER(org_hash_batches)},
113 {P_HashBatches, "V" ,"Hash Batches", NULL, false, NULL, SETTER(hash_batches)},
114 {P_HashBuckets, "W" ,"Hash Buckets", NULL, false, NULL, SETTER(hash_buckets)},
115 {P_RowsFilterRmvd, "X" ,"Rows Removed by Filter",NULL,false,NULL, SETTER(filter_removed)},
116 {P_RowsIdxRchkRmvd, "Y" ,"Rows Removed by Index Recheck",NULL,false, NULL, SETTER(idxrchk_removed)},
117 {P_TrgTime, "Z" ,"Time", NULL, false, NULL, SETTER(trig_time)},
118 {P_TrgCalls, "z" ,"Calls", NULL, false, NULL, SETTER(trig_calls)},
119 {P_PlanTime, "#" ,"Planning Time", NULL, false, NULL, SETTER(plan_time)},
120 {P_ExecTime, "$" ,"Execution Time", NULL, false, NULL, SETTER(exec_time)},
121 {P_ExactHeapBlks, "&" ,"Exact Heap Blocks", NULL, false, NULL, SETTER(exact_heap_blks)},
122 {P_LossyHeapBlks, "(" ,"Lossy Heap Blocks", NULL, false, NULL, SETTER(lossy_heap_blks)},
123 {P_RowsJoinFltRemvd,")" ,"Rows Removed by Join Filter", NULL, false, NULL, SETTER(joinfilt_removed)},
124 {P_Invalid, NULL, NULL, NULL, false, NULL, NULL}
127 word_table nodetypes[] =
129 {T_Result, "a" ,"Result", NULL, false, NULL, NULL},
130 {T_ModifyTable, "b" ,"ModifyTable", NULL, false, NULL, NULL},
131 {T_Append, "c" ,"Append", NULL, false, NULL, NULL},
132 {T_MergeAppend, "d" ,"Merge Append", NULL, false, NULL, NULL},
133 {T_RecursiveUnion,"e" ,"Recursive Union",NULL, false, NULL, NULL},
134 {T_BitmapAnd, "f" ,"BitmapAnd", NULL, false, NULL, NULL},
135 {T_BitmapOr, "g" ,"BitmapOr", NULL, false, NULL, NULL},
136 {T_Scan, "" , "", "", false, NULL, NULL},
137 {T_SeqScan, "h" ,"Seq Scan", NULL, false, NULL, NULL},
138 {T_IndexScan, "i" ,"Index Scan", NULL, false, NULL, NULL},
139 {T_IndexOnlyScan,"j","Index Only Scan",NULL, false, NULL, NULL},
140 {T_BitmapIndexScan,"k" ,"Bitmap Index Scan", NULL, false, NULL, NULL},
141 {T_BitmapHeapScan,"l" ,"Bitmap Heap Scan", NULL ,false, NULL, NULL},
142 {T_TidScan, "m" ,"Tid Scan", NULL, false, NULL, NULL},
143 {T_SubqueryScan,"n" ,"Subquery Scan", NULL, false, NULL, NULL},
144 {T_FunctionScan,"o" ,"Function Scan", NULL, false, NULL, NULL},
145 {T_ValuesScan, "p" ,"Values Scan", NULL, false, NULL, NULL},
146 {T_CteScan, "q" ,"CTE Scan", NULL, false, NULL, NULL},
147 {T_WorkTableScan,"r","Workable Scan", NULL, false, NULL, NULL},
148 {T_ForeignScan, "s" , "Foreign Scan", NULL, false, NULL, NULL},
149 {T_Join, "" , "", NULL, false, NULL, NULL},
150 {T_NestLoop, "t" ,"Nested Loop", NULL, false, NULL, NULL},
151 {T_MergeJoin, "u" ,"Merge Join", "Merge", false, NULL, NULL},
152 {T_HashJoin, "v" ,"Hash Join", "Hash", false, NULL, NULL},
153 {T_Material, "w" ,"Materialize", NULL, false, NULL, NULL},
154 {T_Sort, "x" ,"Sort", NULL, false, NULL, NULL},
155 {T_Group, "y" ,"Group", NULL, false, NULL, NULL},
156 {T_Agg, "z" ,"Aggregate", NULL, false, NULL, NULL},
157 {T_WindowAgg, "0" ,"WindowAgg", NULL, false, NULL, NULL},
158 {T_Unique, "1" ,"Unique", NULL, false, NULL, NULL},
159 {T_Hash, "2" ,"Hash", NULL, false, NULL, NULL},
160 {T_SetOp, "3" ,"SetOp", NULL, false, NULL, NULL},
161 {T_LockRows, "4" ,"LockRows", NULL, false, NULL, NULL},
162 {T_Limit, "5" ,"Limit", NULL, false, NULL, NULL},
163 {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
166 word_table directions[] =
168 {T_Invalid, "b" ,"Backward", "Backward", false, NULL, NULL},
169 {T_Invalid, "n" ,"NoMovement","", false, NULL, NULL},
170 {T_Invalid, "f" ,"Forward", "", false, NULL, NULL},
171 {T_Invalid, NULL , NULL, NULL, false, NULL, NULL}
174 word_table relationships[] =
176 {T_Invalid, "o" ,"Outer", NULL, false, NULL, NULL},
177 {T_Invalid, "i" ,"Inner", NULL, false, NULL, NULL},
178 {T_Invalid, "s" ,"Subquery", NULL, false, NULL, NULL},
179 {T_Invalid, "m" ,"Member", NULL, false, NULL, NULL},
180 {T_Invalid, "I" ,"InitPlan", NULL, false, NULL, NULL},
181 {T_Invalid, "S" ,"SubPlan", NULL, false, NULL, NULL},
182 {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
185 word_table strategies[] =
187 {S_Plain, "p" ,"Plain", NULL, false, NULL, NULL},
188 {S_Sorted, "s" ,"Sorted", NULL, false, NULL, NULL},
189 {S_Hashed, "h" ,"Hashed", NULL, false, NULL, NULL},
190 {S_Invalid, NULL, NULL, NULL, false, NULL, NULL}
193 word_table operations[] =
195 {T_Invalid, "i" ,"Insert", NULL, false, NULL, NULL},
196 {T_Invalid, "d" ,"Delete", NULL, false, NULL, NULL},
197 {T_Invalid, "u" ,"Update", NULL, false, NULL, NULL},
198 {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
201 word_table jointypes[] =
203 {T_Invalid, "i" ,"Inner", NULL, false, NULL, NULL},
204 {T_Invalid, "l" ,"Left", NULL, false, NULL, NULL},
205 {T_Invalid, "f" ,"Full", NULL, false, NULL, NULL},
206 {T_Invalid, "r" ,"Right", NULL, false, NULL, NULL},
207 {T_Invalid, "s" ,"Semi", NULL, false, NULL, NULL},
208 {T_Invalid, "a" ,"Anti", NULL, false, NULL, NULL},
209 {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
212 word_table setsetopcommands[] =
214 {T_Invalid, "i" ,"Intersect", NULL, false, NULL, NULL},
215 {T_Invalid, "I" ,"Intersect All", NULL, false, NULL, NULL},
216 {T_Invalid, "e" ,"Except", NULL, false, NULL, NULL},
217 {T_Invalid, "E" ,"Except All", NULL, false, NULL, NULL},
218 {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
221 word_table sortmethods[] =
223 {T_Invalid, "h" ,"top-N heapsort", NULL, false, NULL, NULL},
224 {T_Invalid, "q" ,"quicksort", NULL, false, NULL, NULL},
225 {T_Invalid, "e" ,"external sort", NULL, false, NULL, NULL},
226 {T_Invalid, "E" ,"external merge", NULL, false, NULL, NULL},
227 {T_Invalid, "s" ,"still in progress", NULL, false, NULL, NULL},
228 {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
231 word_table sortspacetype[] =
233 {T_Invalid, "d" ,"Disk", NULL, false, NULL, NULL},
234 {T_Invalid, "m" ,"Memory",NULL, false, NULL, NULL},
235 {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
239 search_word_table(word_table *tbl, const char *word, int mode)
244 (mode == PGSP_JSON_SHORTEN || mode == PGSP_JSON_NORMALIZE);
248 * Use simple linear search. We can gain too small portion of the whole
249 * processing time using more 'clever' algorithms like b-tree or tries,
250 * which won't be worth the additional memory, complexity and
251 * initialization cost.
253 for (p = tbl ; p->longname ; p++)
255 if (strcmp(longname ? p->longname: p->shortname, word) == 0)
259 if (p->longname == NULL && mode == PGSP_JSON_TEXTIZE)
261 /* Fallback to long json prop name */
262 for (p = tbl ; p->longname ; p++)
263 if (strcmp(p->longname, word) == 0)
267 return (p->longname ? p : NULL);
272 coverter_core(word_table *tbl,
273 const char *src, pgsp_parser_mode mode)
278 p = search_word_table(tbl, src, mode);
285 case PGSP_JSON_SHORTEN:
286 case PGSP_JSON_NORMALIZE:
289 case PGSP_JSON_INFLATE:
290 case PGSP_JSON_YAMLIZE:
291 case PGSP_JSON_XMLIZE:
294 case PGSP_JSON_TEXTIZE:
301 elog(ERROR, "Internal error");
307 conv_nodetype(const char *src, pgsp_parser_mode mode)
309 return coverter_core(nodetypes, src, mode);
313 conv_scandir(const char *src, pgsp_parser_mode mode)
315 return coverter_core(directions, src, mode);
319 conv_relasionship(const char *src, pgsp_parser_mode mode)
321 return coverter_core(relationships, src, mode);
325 conv_strategy(const char *src, pgsp_parser_mode mode)
327 return coverter_core(strategies, src, mode);
331 * Look for these operator characters in order to decide whether to strip
332 * whitespaces which are needless from the view of sql syntax in
333 * normalize_expr(). This must be synced with op_chars in scan.l.
335 #define OPCHARS "~!@#^&|`?+-*/%<>="
336 #define IS_WSCHAR(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')
337 #define IS_CONST(tok) (tok == FCONST || tok == SCONST || tok == BCONST || \
338 tok == XCONST || tok == ICONST || tok == NULL_P || \
339 tok == TRUE_P || tok == FALSE_P || \
340 tok == CURRENT_DATE || tok == CURRENT_TIME || \
341 tok == LOCALTIME || tok == LOCALTIMESTAMP)
344 * norm_yylex: core_yylex with replacing some tokens.
347 norm_yylex(char *str, core_YYSTYPE *yylval, YYLTYPE *yylloc, core_yyscan_t yyscanner)
353 tok = core_yylex(yylval, yylloc, yyscanner);
358 * Error might occur during parsing quoted tokens that chopped
359 * halfway. Just ignore the rest of this query even if there might
360 * be other reasons for parsing to fail.
368 * '?' alone is assumed to be an IDENT. If there's a real
369 * operator '?', this should be confused but there's hardly be.
371 if (tok == Op && str[*yylloc] == '?' &&
372 strchr(OPCHARS, str[*yylloc + 1]) == NULL)
376 * Replace tokens with '=' if the operator is consists of two or
377 * more opchars only. Assuming that opchars do not compose a token
378 * with non-opchars, check the first char only is sufficient.
380 if (tok == Op && strchr(OPCHARS, str[*yylloc]) != NULL)
387 * normalize_expr - Normalize statements or expressions.
389 * Mask constants, strip unnecessary whitespaces and upcase keywords. expr is
390 * modified in-place (destructively). If readablity is more important than
391 * uniqueness, preserve_space puts one space for one existent whitespace for
395 normalize_expr(char *expr, bool preserve_space)
397 core_yyscan_t yyscanner;
398 core_yy_extra_type yyextra;
407 yyscanner = scanner_init(expr,
417 tok = norm_yylex(expr, &yylval, &yylloc, yyscanner);
425 /* Skipping preceding whitespaces */
426 for(i = lastloc ; i < start && IS_WSCHAR(expr[i]) ; i++);
428 /* Searching for trailing whitespace */
429 for(i2 = i; i2 < start && !IS_WSCHAR(expr[i2]) ; i2++);
431 if (lasttok == IDENT)
433 /* Identifiers are copied in case-sensitive manner. */
434 memcpy(wp, expr + i, i2 - i);
439 /* Upcase keywords */
441 for (sp = expr + i ; sp < expr + i2 ; sp++, wp++)
442 *wp = (*sp >= 'a' && *sp <= 'z' ?
443 *sp - ('a' - 'A') : *sp);
447 * Because of destructive writing, wp must not go advance the
449 * Altough this function's output does not need any validity as a
450 * statement or an expression, spaces are added where it should be
451 * to keep some extent of sanity. If readablity is more important
452 * than uniqueness, preserve_space adds one space for each
453 * existent whitespace.
458 (tok >= IDENT && lasttok >= IDENT &&
459 !IS_CONST(tok) && !IS_CONST(lasttok))))
466 /* Exit on parse error. */
474 * Negative signs before numbers are tokenized separately. And
475 * explicit positive signs won't appear in deparsed expressions.
478 tok = norm_yylex(expr, &yylval, &yylloc, yyscanner);
480 /* Exit on parse error. */
491 tok = norm_yylex(expr, &yylval, &end, yyscanner);
493 /* Exit on parse error. */
501 * Negative values may be surrounded with parens by the
502 * deparser. Mask involving them.
504 if (lasttok == '(' && tok == ')')
510 while (expr[end - 1] == ' ') end--;
526 conv_expression(const char *src, pgsp_parser_mode mode)
528 const char *ret = src;
530 if (mode == PGSP_JSON_NORMALIZE)
532 char *t = pstrdup(src);
533 normalize_expr(t, true);
534 ret = (const char *)t;
540 conv_operation(const char *src, pgsp_parser_mode mode)
542 return coverter_core(operations, src, mode);
547 conv_jointype(const char *src, pgsp_parser_mode mode)
549 return coverter_core(jointypes, src, mode);
553 conv_setsetopcommand(const char *src, pgsp_parser_mode mode)
555 return coverter_core(setsetopcommands, src, mode);
559 conv_sortmethod(const char *src, pgsp_parser_mode mode)
561 return coverter_core(sortmethods, src, mode);
565 conv_sortspacetype(const char *src, pgsp_parser_mode mode)
567 return coverter_core(sortspacetype, src, mode);
570 /**** Parser callbacks ****/
574 json_objstart(void *state)
576 pgspParserContext *ctx = (pgspParserContext *)state;
578 if (ctx->mode == PGSP_JSON_INFLATE)
580 if (!ctx->fname && ctx->dest->len > 0)
582 appendStringInfoChar(ctx->dest, '\n');
583 appendStringInfoSpaces(ctx->dest, (ctx->level) * INDENT_STEP);
587 appendStringInfoChar(ctx->dest, '{');
590 ctx->first = bms_add_member(ctx->first, ctx->level);
592 if (ctx->mode == PGSP_JSON_INFLATE)
593 appendStringInfoChar(ctx->dest, '\n');
597 json_objend(void *state)
599 pgspParserContext *ctx = (pgspParserContext *)state;
600 if (ctx->mode == PGSP_JSON_INFLATE)
602 if (!bms_is_member(ctx->level, ctx->first))
603 appendStringInfoChar(ctx->dest, '\n');
604 appendStringInfoSpaces(ctx->dest, (ctx->level - 1) * INDENT_STEP);
607 appendStringInfoChar(ctx->dest, '}');
610 ctx->last_elem_is_object = true;
611 ctx->first = bms_del_member(ctx->first, ctx->level);
616 json_arrstart(void *state)
618 pgspParserContext *ctx = (pgspParserContext *)state;
620 appendStringInfoChar(ctx->dest, '[');
623 ctx->last_elem_is_object = true;
624 ctx->first = bms_add_member(ctx->first, ctx->level);
628 json_arrend(void *state)
630 pgspParserContext *ctx = (pgspParserContext *)state;
631 if (ctx->mode == PGSP_JSON_INFLATE &&
632 ctx->last_elem_is_object)
634 appendStringInfoChar(ctx->dest, '\n');
635 appendStringInfoSpaces(ctx->dest, (ctx->level - 1) * INDENT_STEP);
638 appendStringInfoChar(ctx->dest, ']');
643 json_ofstart(void *state, char *fname, bool isnull)
646 pgspParserContext *ctx = (pgspParserContext *)state;
650 p = search_word_table(propfields, fname, ctx->mode);
654 (errmsg("JSON parser encoutered unknown field name: \"%s\".", fname),
655 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
658 ctx->remove = (ctx->mode == PGSP_JSON_NORMALIZE &&
659 (!p || !p->normalize_use));
664 if (!bms_is_member(ctx->level, ctx->first))
666 appendStringInfoChar(ctx->dest, ',');
667 if (ctx->mode == PGSP_JSON_INFLATE)
668 appendStringInfoChar(ctx->dest, '\n');
671 ctx->first = bms_del_member(ctx->first, ctx->level);
673 if (ctx->mode == PGSP_JSON_INFLATE)
674 appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);
676 if (!p || !p->longname)
678 else if (ctx->mode == PGSP_JSON_INFLATE)
683 escape_json(ctx->dest, fn);
685 ctx->valconverter = (p ? p->converter : NULL);
687 appendStringInfoChar(ctx->dest, ':');
689 if (ctx->mode == PGSP_JSON_INFLATE)
690 appendStringInfoChar(ctx->dest, ' ');
694 json_aestart(void *state, bool isnull)
696 pgspParserContext *ctx = (pgspParserContext *)state;
700 if (!bms_is_member(ctx->level, ctx->first))
702 appendStringInfoChar(ctx->dest, ',');
703 if (ctx->mode == PGSP_JSON_INFLATE &&
704 !ctx->last_elem_is_object)
705 appendStringInfoChar(ctx->dest, ' ');
708 ctx->first = bms_del_member(ctx->first, ctx->level);
712 json_scalar(void *state, char *token, JsonTokenType tokentype)
714 pgspParserContext *ctx = (pgspParserContext *)state;
715 const char *val = token;
720 if (ctx->valconverter)
721 val = ctx->valconverter(token, ctx->mode);
723 if (tokentype == JSON_TOKEN_STRING)
724 escape_json(ctx->dest, val);
726 appendStringInfoString(ctx->dest, val);
727 ctx->last_elem_is_object = false;
733 yaml_objstart(void *state)
735 pgspParserContext *ctx = (pgspParserContext *)state;
739 if (ctx->dest->len > 0)
740 appendStringInfoChar(ctx->dest, '\n');
741 appendStringInfoSpaces(ctx->dest, (ctx->level - 1) * INDENT_STEP);
742 appendStringInfoString(ctx->dest, "- ");
743 appendStringInfoString(ctx->dest, ctx->fname);
744 appendStringInfoString(ctx->dest, ":\n");
745 appendStringInfoSpaces(ctx->dest, (ctx->level + 1) * INDENT_STEP);
750 ctx->first = bms_add_member(ctx->first, ctx->level);
754 yaml_objend(void *state)
756 pgspParserContext *ctx = (pgspParserContext *)state;
759 ctx->last_elem_is_object = true;
760 ctx->first = bms_del_member(ctx->first, ctx->level);
764 yaml_arrstart(void *state)
766 pgspParserContext *ctx = (pgspParserContext *)state;
770 appendStringInfoString(ctx->dest, ctx->fname);
771 appendStringInfoString(ctx->dest, ":");
776 ctx->first = bms_add_member(ctx->first, ctx->level);
780 yaml_arrend(void *state)
782 pgspParserContext *ctx = (pgspParserContext *)state;
786 yaml_ofstart(void *state, char *fname, bool isnull)
789 pgspParserContext *ctx = (pgspParserContext *)state;
792 p = search_word_table(propfields, fname, ctx->mode);
796 (errmsg("Short JSON parser encoutered unknown field name: \"%s\".", fname),
797 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
799 s = (p ? p->longname : fname);
801 if (!bms_is_member(ctx->level, ctx->first))
803 appendStringInfoString(ctx->dest, "\n");
804 appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);
807 ctx->first = bms_del_member(ctx->first, ctx->level);
809 ctx->valconverter = NULL;
811 ctx->valconverter = (p ? p->converter : NULL);
815 yaml_aestart(void *state, bool isnull)
817 pgspParserContext *ctx = (pgspParserContext *)state;
819 appendStringInfoString(ctx->dest, "\n");
820 bms_del_member(ctx->first, ctx->level);
821 appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);
822 appendStringInfoString(ctx->dest, "- ");
826 yaml_scalar(void *state, char *token, JsonTokenType tokentype)
828 pgspParserContext *ctx = (pgspParserContext *)state;
832 appendStringInfoString(ctx->dest, ctx->fname);
833 appendStringInfoString(ctx->dest, ": ");
837 json_scalar(state, token, tokentype);
839 ctx->last_elem_is_object = false;
845 xml_objstart(void *state)
847 pgspParserContext *ctx = (pgspParserContext *)state;
850 ctx->first = bms_add_member(ctx->first, ctx->level);
855 xml_objend(void *state)
857 pgspParserContext *ctx = (pgspParserContext *)state;
858 appendStringInfoChar(ctx->dest, '\n');
859 appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);
862 ctx->first = bms_del_member(ctx->first, ctx->level);
864 ctx->last_elem_is_object = true;
868 xml_arrend(void *state)
870 pgspParserContext *ctx = (pgspParserContext *)state;
872 appendStringInfoChar(ctx->dest, '\n');
873 appendStringInfoSpaces(ctx->dest, (ctx->level + 1) * INDENT_STEP);
877 adjust_wbuf(pgspParserContext *ctx, int len)
881 for (buflen = ctx->wbuflen ; len > buflen ; buflen *= 2);
882 if (buflen > ctx->wbuflen)
884 ctx->wbuf = (char *)palloc(buflen);
885 ctx->wbuflen = buflen;
890 hyphenate_words(pgspParserContext *ctx, char *src)
894 adjust_wbuf(ctx, strlen(src) + 1);
895 strcpy(ctx->wbuf, src);
897 for (p = ctx->wbuf ; *p ; p++)
898 if (*p == ' ') *p = '-';
904 xml_ofstart(void *state, char *fname, bool isnull)
907 pgspParserContext *ctx = (pgspParserContext *)state;
910 p = search_word_table(propfields, fname, ctx->mode);
914 (errmsg("Short JSON parser encoutered unknown field name: \"%s\".", fname),
915 errdetail_log("INPUT: \"%s\"", ctx->org_string)));
917 s = (p ? p->longname : fname);
920 * save current process context
921 * There's no problem if P_Plan appears recursively.
923 if (p && (p->tag == P_Plan || p->tag == P_Triggers))
924 ctx->processing = p->tag;
926 appendStringInfoChar(ctx->dest, '\n');
927 appendStringInfoSpaces(ctx->dest, (ctx->level + 1) * INDENT_STEP);
929 ctx->valconverter = NULL;
931 appendStringInfoChar(ctx->dest, '<');
932 appendStringInfoString(ctx->dest, escape_xml(hyphenate_words(ctx, s)));
933 appendStringInfoChar(ctx->dest, '>');
934 ctx->valconverter = (p ? p->converter : NULL);
937 * If the object field name is Plan or Triggers, the value should be an
938 * array and the items are tagged by other than "Item". "Item"s appear
939 * only in Output field.
941 if (p && (p->tag == P_Plans || p->tag == P_Triggers))
942 ctx->not_item = bms_add_member(ctx->not_item, ctx->level + 1);
944 ctx->not_item = bms_del_member(ctx->not_item, ctx->level + 1);
948 xml_ofend(void *state, char *fname, bool isnull)
950 pgspParserContext *ctx = (pgspParserContext *)state;
954 p = search_word_table(propfields, fname, ctx->mode);
955 s = (p ? p->longname : fname);
957 appendStringInfoString(ctx->dest, "</");
958 appendStringInfoString(ctx->dest, escape_xml(hyphenate_words(ctx, s)));
959 appendStringInfoChar(ctx->dest, '>');
963 xml_aestart(void *state, bool isnull)
965 pgspParserContext *ctx = (pgspParserContext *)state;
969 * The "Trigger" in "Triggers", "Plan" in "Plans" and "Item" nodes are
970 * implicitly represented in JSON format. Restore them for XML format.
974 if (bms_is_member(ctx->level, ctx->not_item))
976 if (ctx->processing == P_Plan)
984 appendStringInfoChar(ctx->dest, '\n');
985 appendStringInfoSpaces(ctx->dest, (ctx->level + 1) * INDENT_STEP);
986 appendStringInfoString(ctx->dest, tag);
990 xml_aeend(void *state, bool isnull)
992 pgspParserContext *ctx = (pgspParserContext *)state;
996 * The "Plan" in "Plans" or "Item" nodes are implicitly represented in
997 * JSON format. Restore it for XML format.
1000 if (bms_is_member(ctx->level, ctx->not_item))
1002 if (ctx->processing == P_Plan)
1009 appendStringInfoString(ctx->dest, tag);
1014 xml_scalar(void *state, char *token, JsonTokenType tokentype)
1016 pgspParserContext *ctx = (pgspParserContext *)state;
1017 const char *s = token;
1019 if (ctx->valconverter)
1020 s = ctx->valconverter(token, PGSP_JSON_XMLIZE);
1022 if (tokentype == JSON_TOKEN_STRING)
1025 appendStringInfoString(ctx->dest, s);
1026 ctx->last_elem_is_object = false;
1029 /********************************/
1031 init_parser_context(pgspParserContext *ctx, int mode,
1032 char *orgstr, char *buf, int buflen){
1033 memset(ctx, 0, sizeof(*ctx));
1034 ctx->dest = makeStringInfo();
1036 ctx->org_string = orgstr;
1038 ctx->wbuflen = buflen;
1042 * run_pg_parse_json:
1044 * Wrap pg_parse_json in order to restore InterruptHoldoffCount when parse
1047 * Returns true when parse completed. False for unexpected end of string.
1050 run_pg_parse_json(JsonLexContext *lex, JsonSemAction *sem)
1052 MemoryContext ccxt = CurrentMemoryContext;
1053 uint32 saved_IntrHoldoffCount;
1056 * "ereport(ERROR.." occurs on error in pg_parse_json resets
1057 * InterruptHoldoffCount to zero, so we must save the value before calling
1058 * json parser to restore it on parse error. See errfinish().
1060 saved_IntrHoldoffCount = InterruptHoldoffCount;
1064 pg_parse_json(lex, sem);
1071 InterruptHoldoffCount = saved_IntrHoldoffCount;
1073 ecxt = MemoryContextSwitchTo(ccxt);
1074 errdata = CopyErrorData();
1076 if (errdata->sqlerrcode == ERRCODE_INVALID_TEXT_REPRESENTATION)
1083 MemoryContextSwitchTo(ecxt);
1093 init_json_lex_context(JsonLexContext *lex, char *json)
1095 lex->input = lex->token_terminator = lex->line_start = json;
1096 lex->line_number = 1;
1097 lex->input_length = strlen(json);
1098 lex->strval = makeStringInfo();
1102 init_json_semaction(JsonSemAction *sem, pgspParserContext *ctx)
1104 sem->semstate = (void*)ctx;
1105 sem->object_start = json_objstart;
1106 sem->object_end = json_objend;
1107 sem->array_start = json_arrstart;
1108 sem->array_end = json_arrend;
1109 sem->object_field_start = json_ofstart;
1110 sem->object_field_end = NULL;
1111 sem->array_element_start= json_aestart;
1112 sem->array_element_end = NULL;
1113 sem->scalar = json_scalar;
1117 pgsp_json_shorten(char *json)
1121 pgspParserContext ctx;
1123 init_json_lex_context(&lex, json);
1124 init_parser_context(&ctx, PGSP_JSON_SHORTEN, json, NULL, 0);
1125 init_json_semaction(&sem, &ctx);
1127 run_pg_parse_json(&lex, &sem);
1129 return ctx.dest->data;
1133 pgsp_json_normalize(char *json)
1137 pgspParserContext ctx;
1139 init_json_lex_context(&lex, json);
1140 init_parser_context(&ctx,PGSP_JSON_NORMALIZE, json, NULL, 0);
1141 init_json_semaction(&sem, &ctx);
1143 run_pg_parse_json(&lex, &sem);
1145 return ctx.dest->data;
1149 pgsp_json_inflate(char *json)
1153 pgspParserContext ctx;
1155 init_json_lex_context(&lex, json);
1156 init_parser_context(&ctx, PGSP_JSON_INFLATE, json, NULL, 0);
1157 init_json_semaction(&sem, &ctx);
1159 if (!run_pg_parse_json(&lex, &sem))
1161 if (ctx.dest->len > 0 &&
1162 ctx.dest->data[ctx.dest->len - 1] != '\n')
1163 appendStringInfoChar(ctx.dest, '\n');
1165 if (ctx.dest->len == 0)
1166 appendStringInfoString(ctx.dest, "<Input was not JSON>");
1168 appendStringInfoString(ctx.dest, "<truncated>");
1171 return ctx.dest->data;
1175 pgsp_json_yamlize(char *json)
1177 pgspParserContext ctx;
1181 init_json_lex_context(&lex, json);
1182 init_parser_context(&ctx, PGSP_JSON_YAMLIZE, json, NULL, 0);
1184 sem.semstate = (void*)&ctx;
1185 sem.object_start = yaml_objstart;
1186 sem.object_end = yaml_objend;
1187 sem.array_start = yaml_arrstart;
1188 sem.array_end = yaml_arrend;
1189 sem.object_field_start = yaml_ofstart;
1190 sem.object_field_end = NULL;
1191 sem.array_element_start= yaml_aestart;
1192 sem.array_element_end = NULL;
1193 sem.scalar = yaml_scalar;
1195 if (!run_pg_parse_json(&lex, &sem))
1197 if (ctx.dest->len > 0 &&
1198 ctx.dest->data[ctx.dest->len - 1] != '\n')
1199 appendStringInfoChar(ctx.dest, '\n');
1201 if (ctx.dest->len == 0)
1202 appendStringInfoString(ctx.dest, "<Input was not JSON>");
1204 appendStringInfoString(ctx.dest, "<truncated>");
1207 return ctx.dest->data;
1211 pgsp_json_xmlize(char *json)
1213 pgspParserContext ctx;
1219 init_json_lex_context(&lex, json);
1220 init_parser_context(&ctx, PGSP_JSON_XMLIZE, json, buf, sizeof(buf));
1222 sem.semstate = (void*)&ctx;
1223 sem.object_start = xml_objstart;
1224 sem.object_end = xml_objend;
1225 sem.array_start = NULL;
1226 sem.array_end = xml_arrend;
1227 sem.object_field_start = xml_ofstart;
1228 sem.object_field_end = xml_ofend;
1229 sem.array_element_start= xml_aestart;
1230 sem.array_element_end = xml_aeend;
1231 sem.scalar = xml_scalar;
1233 appendStringInfo(ctx.dest,
1234 "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n <Query>");
1235 start_len = ctx.dest->len;
1237 if (!run_pg_parse_json(&lex, &sem))
1239 if (ctx.dest->len > start_len &&
1240 ctx.dest->data[ctx.dest->len - 1] != '\n')
1241 appendStringInfoChar(ctx.dest, '\n');
1243 if (ctx.dest->len == start_len)
1245 resetStringInfo(ctx.dest);
1246 appendStringInfoString(ctx.dest, "<Input was not JSON>");
1249 appendStringInfoString(ctx.dest, "<truncated>");
1252 appendStringInfo(ctx.dest, "</Query>\n</explain>\n");
1254 return ctx.dest->data;