OSDN Git Service

Allow handled properties not to have a short name
[pgstoreplans/pg_store_plans.git] / pgsp_json.c
1 /*-------------------------------------------------------------------------
2  *
3  * pgsp_json.c: Plan handler for JSON/XML/YAML style plans
4  *
5  * Copyright (c) 2012-2015, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
6  *
7  * IDENTIFICATION
8  *        pg_store_plan/pgsp_json.c
9  *
10  *-------------------------------------------------------------------------
11  */
12
13 #include "postgres.h"
14 #include "miscadmin.h"
15 #include "nodes/nodes.h"
16 #include "nodes/parsenodes.h"
17 #include "nodes/bitmapset.h"
18 #include "parser/scanner.h"
19 #include "parser/gram.h"
20 #include "utils/xml.h"
21 #include "utils/json.h"
22 #include "utils/jsonapi.h"
23
24 #include "pgsp_json.h"
25 #include "pgsp_json_int.h"
26
27 #define INDENT_STEP 2
28
29
30 void normalize_expr(char *expr, bool preserve_space);
31 static const char *converter_core(word_table *tbl,
32                                                  const char *src, pgsp_parser_mode mode);
33
34 static void json_objstart(void *state);
35 static void json_objend(void *state);
36 static void json_arrstart(void *state);
37 static void json_arrend(void *state);
38 static void json_ofstart(void *state, char *fname, bool isnull);
39 static void json_aestart(void *state, bool isnull);
40 static void json_scalar(void *state, char *token, JsonTokenType tokentype);
41
42 static void yaml_objstart(void *state);
43 static void yaml_objend(void *state);
44 static void yaml_arrstart(void *state);
45 static void yaml_arrend(void *state);
46 static void yaml_ofstart(void *state, char *fname, bool isnull);
47 static void yaml_aestart(void *state, bool isnull);
48 static void yaml_scalar(void *state, char *token, JsonTokenType tokentype);
49
50 static void adjust_wbuf(pgspParserContext *ctx, int len);
51 static char *hyphenate_words(pgspParserContext *ctx, char *src);
52 static void xml_objstart(void *state);
53 static void xml_objend(void *state);
54 static void xml_arrend(void *state);
55 static void xml_ofstart(void *state, char *fname, bool isnull);
56 static void xml_ofend(void *state, char *fname, bool isnull);
57 static void xml_aestart(void *state, bool isnull);
58 static void xml_aeend(void *state, bool isnull);
59 static void xml_scalar(void *state, char *token, JsonTokenType tokentype) ;
60
61 static void init_json_semaction(JsonSemAction *sem,
62                                                                                   pgspParserContext *ctx);
63
64 word_table propfields[] =
65 {
66         {P_NodeType,            "t" ,"Node Type",                       NULL, true,  conv_nodetype,             SETTER(node_type)},
67         {P_RelationShip,        "h" ,"Parent Relationship",     NULL, true,  conv_relasionship, NULL},
68         {P_RelationName,        "n" ,"Relation Name",           NULL, true,  NULL,                              SETTER(obj_name)},
69         {P_FunctioName,         "f" ,"Function Name",           NULL, true,  NULL,                              SETTER(obj_name)},
70         {P_IndexName,           "i" ,"Index Name",                      NULL, true,  NULL,                              SETTER(index_name)},
71         {P_CTEName,                     "c" ,"CTE Name",                        NULL, true,  NULL,                              SETTER(obj_name)},
72         {P_TrgRelation,         "w" ,"Relation",                        NULL, true,  NULL,                              SETTER(trig_relation)},
73         {P_Schema,                      "s" ,"Schema",                          NULL, true,  NULL,                              SETTER(schema_name)},
74         {P_Alias,                       "a" ,"Alias",                           NULL, true,  NULL,                              SETTER(alias)},
75         {P_Output,                      "o" ,"Output",                          NULL, true,  conv_expression,   SETTER(output)},
76         {P_ScanDir,                     "d" ,"Scan Direction",          NULL, true,  conv_scandir,              SETTER(scan_dir)},
77         {P_MergeCond,           "m" ,"Merge Cond",                      NULL, true,  conv_expression,   SETTER(merge_cond)},
78         {P_Strategy,            "g" ,"Strategy",                        NULL, true,  conv_strategy,             SETTER(strategy)},
79         {P_JoinType,            "j" ,"Join Type",                       NULL, true,  conv_jointype,             SETTER(join_type)},
80         {P_SortMethod,          "e" ,"Sort Method",                     NULL, true,  conv_sortmethod,   SETTER(sort_method)},
81         {P_SortKey,                     "k" ,"Sort Key",                        NULL, true,  conv_expression,   SETTER(sort_key)},
82         {P_Filter,                      "5" ,"Filter",                          NULL, true,  conv_expression,   SETTER(filter)},
83         {P_JoinFilter,          "6" ,"Join Filter",                     NULL, true,  conv_expression,   SETTER(join_filter)},
84         {P_HashCond,            "7" ,"Hash Cond",                       NULL, true,  conv_expression,   SETTER(hash_cond)},
85         {P_IndexCond,           "8" ,"Index Cond",                      NULL, true,  conv_expression,   SETTER(index_cond)},
86         {P_TidCond,                     "9" ,"TID Cond",                        NULL, true,  conv_expression,   SETTER(tid_cond)},
87         {P_RecheckCond,         "0" ,"Recheck Cond",            NULL, true,  conv_expression,   SETTER(recheck_cond)},
88         {P_Operation,           "!" ,"Operation",                       NULL, true,  conv_operation,    SETTER(operation)},
89         {P_SubplanName,         "q" ,"Subplan Name",            NULL, true,  NULL,                              SETTER(subplan_name)},
90         {P_Command,                     "b" ,"Command",                         NULL, true,  conv_setsetopcommand,SETTER(setopcommand)},
91         {P_Triggers,            "r" ,"Triggers",                        NULL, true,  NULL,                              NULL},
92         {P_Trigger,                     "u" ,"Trigger",                         NULL, true,  NULL,                              SETTER(node_type)},
93         {P_TriggerName,         "v" ,"Trigger Name",            NULL, true,  NULL,                              SETTER(trig_name)},
94         {P_ConstraintName,      "x" ,"Constraint Name",         NULL, true,  NULL,                              NULL},
95         {P_Plans,                       "l" ,"Plans",                           NULL, true,  NULL,                              NULL},
96         {P_Plan,                        "p" ,"Plan",                            NULL, true,  NULL,                              NULL},
97         {P_GroupKey,            "-" ,"Group Key",                       NULL, true,  NULL,                              SETTER(group_key)},
98         {P_GroupSets,           "=" ,"Grouping Sets",           NULL, true,  NULL,                              NULL},
99         {P_GroupKeys,           "\\" ,"Group Keys",                     NULL, true,  NULL,                              SETTER(group_key)},
100                                                                                                                   
101         /* Values of these properties are ignored on normalization */
102         {P_FunctionCall,        "y" ,"Function Call",           NULL, false, NULL,                              SETTER(func_call)},
103         {P_StartupCost,         "1" ,"Startup Cost",            NULL, false, NULL,                              SETTER(startup_cost)},
104         {P_TotalCost,           "2" ,"Total Cost",                      NULL, false, NULL,                              SETTER(total_cost)},
105         {P_PlanRows,            "3" ,"Plan Rows",                       NULL, false, NULL,                              SETTER(plan_rows)},
106         {P_PlanWidth,           "4" ,"Plan Width",                      NULL, false, NULL,                              SETTER(plan_width)},
107         {P_ActualStartupTime,"A","Actual Startup Time", NULL, false, NULL,                              SETTER(actual_startup_time)},
108         {P_ActualTotalTime, "B" ,"Actual Total Time",   NULL, false, NULL,                              SETTER(actual_total_time)},
109         {P_ActualRows,          "C" ,"Actual Rows",                     NULL, false, NULL,                              SETTER(actual_rows)},
110         {P_ActualLoops,         "D" ,"Actual Loops",            NULL, false, NULL,                              SETTER(actual_loops)},
111         {P_HeapFetches,         "E" ,"Heap Fetches",            NULL, false, NULL,                              SETTER(heap_fetches)},
112         {P_SharedHitBlks,       "F" ,"Shared Hit Blocks",       NULL, false, NULL,                              SETTER(shared_hit_blks)},
113         {P_SharedReadBlks,      "G" ,"Shared Read Blocks",      NULL, false, NULL,                              SETTER(shared_read_blks)},
114         {P_SharedDirtiedBlks,"H","Shared Dirtied Blocks",NULL,false, NULL,                              SETTER(shared_dirtied_blks)},
115         {P_SharedWrittenBlks,"I","Shared Written Blocks",NULL,false, NULL,                              SETTER(shared_written_blks)},
116         {P_LocalHitBlks,        "J" ,"Local Hit Blocks",        NULL, false, NULL,                              SETTER(local_hit_blks)},
117         {P_LocalReadBlks,       "K" ,"Local Read Blocks",       NULL, false, NULL,                              SETTER(local_read_blks)},
118         {P_LocalDirtiedBlks,"L" ,"Local Dirtied Blocks",NULL, false, NULL,                              SETTER(local_dirtied_blks)},
119         {P_LocalWrittenBlks,"M" ,"Local Written Blocks",NULL, false, NULL,                              SETTER(local_written_blks)},
120         {P_TempReadBlks,        "N" ,"Temp Read Blocks",        NULL, false, NULL,                              SETTER(temp_read_blks)},
121         {P_TempWrittenBlks, "O" ,"Temp Written Blocks", NULL, false, NULL,                              SETTER(temp_written_blks)},
122         {P_IOReadTime,          "P" ,"I/O Read Time",           NULL, false, NULL,                              SETTER(io_read_time)},
123         {P_IOWwriteTime,        "Q" ,"I/O Write Time",          NULL, false, NULL,                              SETTER(io_write_time)},
124         {P_SortSpaceUsed,       "R" ,"Sort Space Used",         NULL, false, NULL,                              SETTER(sort_space_used)},
125         {P_SortSpaceType,       "S" ,"Sort Space Type",         NULL, false, conv_sortspacetype,SETTER(sort_space_type)},
126         {P_PeakMemoryUsage,     "T" ,"Peak Memory Usage",       NULL, false, NULL,                              SETTER(peak_memory_usage)},
127         {P_OrgHashBatches,      "U","Original Hash Batches",NULL, false, NULL,                          SETTER(org_hash_batches)},
128         {P_OrgHashBuckets,      "*","Original Hash Buckets",NULL, false, NULL,                          SETTER(org_hash_buckets)},
129         {P_HashBatches,         "V" ,"Hash Batches",            NULL, false, NULL,                              SETTER(hash_batches)},
130         {P_HashBuckets,         "W" ,"Hash Buckets",            NULL, false, NULL,                              SETTER(hash_buckets)},
131         {P_RowsFilterRmvd,      "X" ,"Rows Removed by Filter",NULL,false,NULL,                          SETTER(filter_removed)},
132         {P_RowsIdxRchkRmvd, "Y" ,"Rows Removed by Index Recheck",NULL,false, NULL,              SETTER(idxrchk_removed)},
133         {P_TrgTime,                     "Z" ,"Time",                            NULL, false,  NULL,                             SETTER(trig_time)},
134         {P_TrgCalls,            "z" ,"Calls",                           NULL, false,  NULL,                             SETTER(trig_calls)},
135         {P_PlanTime,            "#" ,"Planning Time",           NULL, false,  NULL,                             SETTER(plan_time)},
136         {P_ExecTime,            "$" ,"Execution Time",          NULL, false,  NULL,                             SETTER(exec_time)},
137         {P_ExactHeapBlks,       "&" ,"Exact Heap Blocks",       NULL, false,  NULL,                             SETTER(exact_heap_blks)},
138         {P_LossyHeapBlks,       "(" ,"Lossy Heap Blocks",       NULL, false,  NULL,                             SETTER(lossy_heap_blks)},
139         {P_RowsJoinFltRemvd,")" ,"Rows Removed by Join Filter", NULL, false,  NULL,             SETTER(joinfilt_removed)},
140         {P_TargetTables,    "_" ,"Target Tables",               NULL, false,  NULL,                             NULL},
141         {P_ConfRes,                     "%" ,"Conflict Resolution",     NULL, false,  NULL,                     SETTER(conflict_resolution)},
142         {P_ConfArbitIdx,    "@" ,"Conflict Arbiter Indexes",NULL, false,  NULL,                 SETTER(conflict_arbiter_indexes)},
143         {P_TuplesInserted,  "^" ,"Tuples Inserted",             NULL, false,  NULL,                             SETTER(tuples_inserted)},
144         {P_ConfTuples,          "+" ,"Conflicting Tuples",      NULL, false,  NULL,                             SETTER(conflicting_tuples)},
145         {P_Invalid, NULL, NULL, NULL, false, NULL, NULL}
146 };
147
148 word_table nodetypes[] =
149 {
150         {T_Result,              "a" ,"Result",                  NULL, false, NULL, NULL},
151         {T_ModifyTable, "b" ,"ModifyTable",     NULL, false, NULL, NULL},
152         {T_Append,              "c" ,"Append",                  NULL, false, NULL, NULL},
153         {T_MergeAppend, "d" ,"Merge Append",    NULL, false, NULL, NULL},
154         {T_RecursiveUnion,"e" ,"Recursive Union",NULL, false, NULL, NULL},
155         {T_BitmapAnd,   "f" ,"BitmapAnd",               NULL, false, NULL, NULL},
156         {T_BitmapOr,    "g" ,"BitmapOr",                NULL, false, NULL, NULL},
157         {T_Scan,                ""  , "", "", false, NULL, NULL},
158         {T_SeqScan,             "h" ,"Seq Scan",                NULL, false, NULL, NULL},
159         {T_IndexScan,   "i" ,"Index Scan",              NULL, false, NULL, NULL},
160         {T_IndexOnlyScan,"j","Index Only Scan",NULL, false, NULL, NULL},
161         {T_BitmapIndexScan,"k" ,"Bitmap Index Scan", NULL, false, NULL, NULL},
162         {T_BitmapHeapScan,"l" ,"Bitmap Heap Scan", NULL ,false, NULL, NULL},
163         {T_TidScan,             "m" ,"Tid Scan",                NULL, false, NULL, NULL},
164         {T_SubqueryScan,"n" ,"Subquery Scan",   NULL, false, NULL, NULL},
165         {T_FunctionScan,"o" ,"Function Scan",   NULL, false, NULL, NULL},
166         {T_ValuesScan,  "p" ,"Values Scan",     NULL, false, NULL, NULL},
167         {T_CteScan,             "q" ,"CTE Scan",                NULL, false, NULL, NULL},
168         {T_WorkTableScan,"r","WorkTable Scan",  NULL, false, NULL, NULL},
169         {T_ForeignScan, "s" , "Foreign Scan",   NULL, false, NULL, NULL},
170         {T_Join,                ""  ,   "",                             NULL, false, NULL, NULL},
171         {T_NestLoop,    "t" ,"Nested Loop",     NULL, false, NULL, NULL},
172         {T_MergeJoin,   "u" ,"Merge Join",              "Merge", false, NULL, NULL},
173         {T_HashJoin,    "v" ,"Hash Join",               "Hash", false, NULL, NULL},
174         {T_Material,    "w" ,"Materialize",     NULL, false, NULL, NULL},
175         {T_Sort,                "x" ,"Sort",                    NULL, false, NULL, NULL},
176         {T_Group,               "y" ,"Group",                   NULL, false, NULL, NULL},
177         {T_Agg,                 "z" ,"Aggregate",               NULL, false, NULL, NULL},
178         {T_WindowAgg,   "0" ,"WindowAgg",               NULL, false, NULL, NULL},
179         {T_Unique,              "1" ,"Unique",                  NULL, false, NULL, NULL},
180         {T_Hash,                "2" ,"Hash",                    NULL, false, NULL, NULL},
181         {T_SetOp,               "3" ,"SetOp",                   NULL, false, NULL, NULL},
182         {T_LockRows,    "4" ,"LockRows",                NULL, false, NULL, NULL},
183         {T_Limit,               "5" ,"Limit",                   NULL, false, NULL, NULL},
184         {T_Invalid,             NULL, NULL, NULL, false, NULL, NULL}
185 };
186
187 word_table directions[] =
188 {
189         {T_Invalid,  "b" ,"Backward",   "Backward", false, NULL, NULL},
190         {T_Invalid,  "n" ,"NoMovement","",                      false, NULL, NULL},
191         {T_Invalid,  "f" ,"Forward",    "",                     false, NULL, NULL},
192         {T_Invalid, NULL , NULL,                NULL,           false, NULL, NULL}
193 };
194
195 word_table relationships[] =
196 {
197         {T_Invalid,  "o" ,"Outer", NULL, false, NULL, NULL},
198         {T_Invalid,  "i" ,"Inner", NULL, false, NULL, NULL},
199         {T_Invalid,  "s" ,"Subquery", NULL, false, NULL, NULL},
200         {T_Invalid,  "m" ,"Member", NULL, false, NULL, NULL},
201         {T_Invalid,  "I" ,"InitPlan", NULL, false, NULL, NULL},
202         {T_Invalid,  "S" ,"SubPlan", NULL, false, NULL, NULL},
203         {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
204 };
205
206 word_table strategies[] =
207 {
208         {S_Plain,       "p" ,"Plain", NULL, false, NULL, NULL},
209         {S_Sorted,      "s" ,"Sorted", NULL, false, NULL, NULL},
210         {S_Hashed,      "h" ,"Hashed", NULL, false, NULL, NULL},
211         {S_Invalid,     NULL, NULL, NULL, false, NULL, NULL}
212 };
213
214 word_table operations[] =
215 {
216         {T_Invalid,  "i" ,"Insert", NULL, false, NULL, NULL},
217         {T_Invalid,  "d" ,"Delete", NULL, false, NULL, NULL},
218         {T_Invalid,  "u" ,"Update", NULL, false, NULL, NULL},
219         {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
220 };
221
222 word_table jointypes[] =
223 {
224         {T_Invalid,  "i" ,"Inner", NULL, false, NULL, NULL},
225         {T_Invalid,  "l" ,"Left", NULL, false, NULL, NULL},
226         {T_Invalid,  "f" ,"Full", NULL, false, NULL, NULL},
227         {T_Invalid,  "r" ,"Right", NULL, false, NULL, NULL},
228         {T_Invalid,  "s" ,"Semi", NULL, false, NULL, NULL},
229         {T_Invalid,  "a" ,"Anti", NULL, false, NULL, NULL},
230         {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
231 };
232
233 word_table setsetopcommands[] =
234 {
235         {T_Invalid,  "i" ,"Intersect", NULL, false, NULL, NULL},
236         {T_Invalid,  "I" ,"Intersect All", NULL, false, NULL, NULL},
237         {T_Invalid,  "e" ,"Except", NULL, false, NULL, NULL},
238         {T_Invalid,  "E" ,"Except All", NULL, false, NULL, NULL},
239         {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
240 };
241
242 word_table sortmethods[] =
243 {
244         {T_Invalid,  "h" ,"top-N heapsort", NULL, false, NULL, NULL},
245         {T_Invalid,  "q" ,"quicksort", NULL, false, NULL, NULL},
246         {T_Invalid,  "e" ,"external sort", NULL, false, NULL, NULL},
247         {T_Invalid,  "E" ,"external merge", NULL, false, NULL, NULL},
248         {T_Invalid,  "s" ,"still in progress", NULL, false, NULL, NULL},
249         {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
250 };
251
252 word_table sortspacetype[] =
253 {
254         {T_Invalid,  "d" ,"Disk",       NULL, false, NULL, NULL},
255         {T_Invalid,  "m" ,"Memory",NULL, false, NULL, NULL},
256         {T_Invalid, NULL, NULL, NULL, false, NULL, NULL}
257 };
258
259 word_table *
260 search_word_table(word_table *tbl, const char *word, int mode)
261 {
262         word_table *p;
263         
264         bool longname =
265                 (mode == PGSP_JSON_SHORTEN || mode == PGSP_JSON_NORMALIZE);
266
267
268         /*
269          * Use simple linear search. We can gain too small portion of the whole
270          * processing time using more 'clever' algorithms like b-tree or tries,
271          * which won't be worth the additional memory, complexity and
272          * initialization cost.
273          */
274         for (p = tbl ; p->longname ; p++)
275         {
276                 if (strcmp(longname ? p->longname: p->shortname, word) == 0)
277                         break;
278         }
279
280         if (p->longname == NULL && mode == PGSP_JSON_TEXTIZE)
281         {
282                 /* Fallback to long json prop name */
283                 for (p = tbl ; p->longname ; p++)
284                         if (strcmp(p->longname, word) == 0)
285                                 break;
286         }
287
288         return (p->longname ? p : NULL);
289 }
290
291
292 const char *
293 converter_core(word_table *tbl,
294                            const char *src, pgsp_parser_mode mode)
295 {
296         word_table *p;
297         char *ret;
298
299         p = search_word_table(tbl, src, mode);
300
301         if (!p) return src;
302
303         ret = p->shortname;
304         switch(mode)
305         {
306                 case PGSP_JSON_SHORTEN:
307                 case PGSP_JSON_NORMALIZE:
308                         ret = p->shortname;
309                         break;
310                 case PGSP_JSON_INFLATE:
311                 case PGSP_JSON_YAMLIZE:
312                 case PGSP_JSON_XMLIZE:
313                         ret = p->longname;
314                         break;
315                 case PGSP_JSON_TEXTIZE:
316                         if(p->textname)
317                                 ret = p->textname;
318                         else
319                                 ret = p->longname;
320                         break;
321                 default:
322                         elog(ERROR, "Internal error");
323         }
324         return ret;
325 }
326
327 const char *
328 conv_nodetype(const char *src, pgsp_parser_mode mode)
329 {
330         return converter_core(nodetypes, src, mode);
331 }
332
333 const char *
334 conv_scandir(const char *src, pgsp_parser_mode mode)
335 {
336         return converter_core(directions, src, mode);
337 }
338
339 const char *
340 conv_relasionship(const char *src, pgsp_parser_mode mode)
341 {
342         return converter_core(relationships, src, mode);
343 }
344
345 const char *
346 conv_strategy(const char *src, pgsp_parser_mode mode)
347 {
348         return converter_core(strategies, src, mode);
349 }
350
351 /*
352  * Look for these operator characters in order to decide whether to strip
353  * whitespaces which are needless from the view of sql syntax in
354  * normalize_expr(). This must be synced with op_chars in scan.l.
355  */
356 #define OPCHARS "~!@#^&|`?+-*/%<>="
357 #define IS_WSCHAR(c) ((c) == ' ' || (c) == '\n' || (c) == '\t')
358 #define IS_CONST(tok) (tok == FCONST || tok == SCONST || tok == BCONST || \
359                         tok == XCONST || tok == ICONST || tok == NULL_P || \
360                     tok == TRUE_P || tok == FALSE_P || \
361                         tok == CURRENT_DATE || tok == CURRENT_TIME || \
362                     tok == LOCALTIME || tok == LOCALTIMESTAMP)
363
364 /*
365  * norm_yylex: core_yylex with replacing some tokens.
366  */
367 static int
368 norm_yylex(char *str, core_YYSTYPE *yylval, YYLTYPE *yylloc, core_yyscan_t yyscanner)
369 {
370         int tok;
371
372         PG_TRY();
373         {
374                 tok = core_yylex(yylval, yylloc, yyscanner);
375         }
376         PG_CATCH();
377         {
378                 /*
379                  * Error might occur during parsing quoted tokens that chopped
380                  * halfway. Just ignore the rest of this query even if there might
381                  * be other reasons for parsing to fail.
382                  */
383                 FlushErrorState();
384                 return -1;
385         }
386         PG_END_TRY();
387         
388         /*
389          * '?' alone is assumed to be an IDENT.  If there's a real
390          * operator '?', this should be confused but there's hardly be.
391          */
392         if (tok == Op && str[*yylloc] == '?' &&
393                 strchr(OPCHARS, str[*yylloc + 1]) == NULL)
394                 tok = SCONST;
395
396         /*
397          * Replace tokens with '=' if the operator is consists of two or
398          * more opchars only. Assuming that opchars do not compose a token
399          * with non-opchars, check the first char only is sufficient.
400          */
401         if (tok == Op && strchr(OPCHARS, str[*yylloc]) != NULL)
402                 tok = '=';
403
404         return tok;
405 }
406
407 /*
408  * normalize_expr - Normalize statements or expressions.
409  *
410  * Mask constants, strip unnecessary whitespaces and upcase keywords. expr is
411  * modified in-place (destructively). If readability is more important than
412  * uniqueness, preserve_space puts one space for one existent whitespace for
413  * more readability.
414  */
415 void
416 normalize_expr(char *expr, bool preserve_space)
417 {
418         core_yyscan_t yyscanner;
419         core_yy_extra_type yyextra;
420         core_YYSTYPE yylval;
421         YYLTYPE         yylloc;
422         YYLTYPE         lastloc;
423         YYLTYPE start;
424         char *wp;
425         int                     tok, lasttok;
426
427         wp = expr;
428         yyscanner = scanner_init(expr,
429                                                          &yyextra,
430                                                          ScanKeywords,
431                                                          NumScanKeywords);
432
433         lasttok = 0;
434         lastloc = -1;
435
436         for (;;)
437         {
438                 tok = norm_yylex(expr, &yylval, &yylloc, yyscanner);
439
440                 start = yylloc;
441
442                 if (lastloc >= 0)
443                 {
444                         int i, i2;
445                         
446                         /* Skipping preceding whitespaces */
447                         for(i = lastloc ; i < start && IS_WSCHAR(expr[i]) ; i++);
448
449                         /* Searching for trailing whitespace */
450                         for(i2 = i; i2 < start && !IS_WSCHAR(expr[i2]) ; i2++);
451
452                         if (lasttok == IDENT)
453                         {
454                                 /* Identifiers are copied in case-sensitive manner. */
455                                 memcpy(wp, expr + i, i2 - i);
456                                 wp += i2 - i;
457                         }
458                         else
459                         {
460                                 /* Upcase keywords */
461                                 char *sp;
462                                 for (sp = expr + i ; sp < expr + i2 ; sp++, wp++)
463                                         *wp = (*sp >= 'a' && *sp <= 'z' ?
464                                                    *sp - ('a' - 'A') : *sp);
465                         }
466
467                         /*
468                          * Because of destructive writing, wp must not go advance the
469                          * reading point.
470                          * Although this function's output does not need any validity as a
471                          * statement or an expression, spaces are added where it should be
472                          * to keep some extent of sanity.  If readability is more important
473                          * than uniqueness, preserve_space adds one space for each
474                          * existent whitespace.
475                          */
476                         if (tok > 0 &&
477                                 i2 < start &&
478                                 (preserve_space || 
479                                  (tok >= IDENT && lasttok >= IDENT &&
480                                   !IS_CONST(tok) && !IS_CONST(lasttok))))
481                                 *wp++ = ' ';
482
483                         start = i2;
484                 }
485
486                 /* Exit on parse error. */
487                 if (tok < 0)
488                 {
489                         *wp = 0;
490                         return;
491                 }
492
493                 /*
494                  * Negative signs before numbers are tokenized separately. And
495                  * explicit positive signs won't appear in deparsed expressions.
496                  */
497                 if (tok == '-')
498                         tok = norm_yylex(expr, &yylval, &yylloc, yyscanner);
499                 
500                 /* Exit on parse error. */
501                 if (tok < 0)
502                 {
503                         *wp = 0;
504                         return;
505                 }
506
507                 if (IS_CONST(tok))
508                 {
509                         YYLTYPE end;
510                         
511                         tok = norm_yylex(expr, &yylval, &end, yyscanner);
512
513                         /* Exit on parse error. */
514                         if (tok < 0)
515                         {
516                                 *wp = 0;
517                                 return;
518                         }
519
520                         /*
521                          * Negative values may be surrounded with parens by the
522                          * deparser. Mask involving them.
523                          */
524                         if (lasttok == '(' && tok == ')')
525                         {
526                                 wp -= (start - lastloc);
527                                 start = lastloc;
528                                 end++;
529                         }
530
531                         while (expr[end - 1] == ' ') end--;                     
532
533                         *wp++ = '?';
534                         yylloc = end;
535                 }
536
537                 if (tok == 0)
538                         break;
539
540                 lasttok = tok;
541                 lastloc = yylloc;
542         }
543         *wp = 0;
544 }
545
546 const char *
547 conv_expression(const char *src, pgsp_parser_mode mode)
548 {
549         const char *ret = src;
550
551         if (mode == PGSP_JSON_NORMALIZE)
552         {
553                 char *t = pstrdup(src);
554                 normalize_expr(t, true);
555                 ret = (const char *)t;
556         }
557         return ret;
558 }
559
560 const char *
561 conv_operation(const char *src, pgsp_parser_mode mode)
562 {
563         return converter_core(operations, src, mode);
564
565 }
566
567 const char *
568 conv_jointype(const char *src, pgsp_parser_mode mode)
569 {
570         return converter_core(jointypes, src, mode);
571 }
572
573 const char *
574 conv_setsetopcommand(const char *src, pgsp_parser_mode mode)
575 {
576         return converter_core(setsetopcommands, src, mode);
577 }
578
579 const char *
580 conv_sortmethod(const char *src, pgsp_parser_mode mode)
581 {
582         return converter_core(sortmethods, src, mode);
583 }
584
585 const char *
586 conv_sortspacetype(const char *src, pgsp_parser_mode mode)
587 {
588         return converter_core(sortspacetype, src, mode);
589 }
590
591 /**** Parser callbacks ****/
592
593 /* JSON */
594 static void
595 json_objstart(void *state)
596 {
597         pgspParserContext *ctx = (pgspParserContext *)state;
598
599         if (ctx->mode == PGSP_JSON_INFLATE)
600         {
601                 if (!ctx->fname && ctx->dest->len > 0)
602                 {
603                         appendStringInfoChar(ctx->dest, '\n');
604                         appendStringInfoSpaces(ctx->dest, (ctx->level) * INDENT_STEP);
605                 }
606                 ctx->fname = NULL;
607         }
608         appendStringInfoChar(ctx->dest, '{');
609
610         ctx->level++;
611         ctx->first = bms_add_member(ctx->first, ctx->level);
612
613         if (ctx->mode == PGSP_JSON_INFLATE)
614                 appendStringInfoChar(ctx->dest, '\n');
615 }
616
617 static void
618 json_objend(void *state)
619 {
620         pgspParserContext *ctx = (pgspParserContext *)state;
621         if (ctx->mode == PGSP_JSON_INFLATE)
622         {
623                 if (!bms_is_member(ctx->level, ctx->first))
624                         appendStringInfoChar(ctx->dest, '\n');
625                 appendStringInfoSpaces(ctx->dest, (ctx->level - 1) * INDENT_STEP);
626         }
627
628         appendStringInfoChar(ctx->dest, '}');
629
630         ctx->level--;
631         ctx->last_elem_is_object = true;
632         ctx->first = bms_del_member(ctx->first, ctx->level);
633         ctx->fname = NULL;
634 }
635
636 static void
637 json_arrstart(void *state)
638 {
639         pgspParserContext *ctx = (pgspParserContext *)state;
640
641         if (ctx->current_list == P_GroupKeys)
642                 ctx->wlist_level++;
643
644         appendStringInfoChar(ctx->dest, '[');
645         ctx->fname = NULL;
646         ctx->level++;
647         ctx->last_elem_is_object = true;
648         ctx->first = bms_add_member(ctx->first, ctx->level);
649 }
650
651 static void
652 json_arrend(void *state)
653 {
654         pgspParserContext *ctx = (pgspParserContext *)state;
655
656         if (ctx->current_list == P_GroupKeys)
657                 ctx->wlist_level--;
658
659         if (ctx->current_list == P_GroupKeys ? ctx->wlist_level == 0 :
660                 (ctx->mode == PGSP_JSON_INFLATE && ctx->last_elem_is_object))
661         {
662                 appendStringInfoChar(ctx->dest, '\n');
663                 appendStringInfoSpaces(ctx->dest, (ctx->level - 1) * INDENT_STEP);
664         }
665
666         appendStringInfoChar(ctx->dest, ']');
667         ctx->level--;
668 }
669
670 static void
671 json_ofstart(void *state, char *fname, bool isnull)
672 {
673         word_table *p;
674         pgspParserContext *ctx = (pgspParserContext *)state;
675         char *fn;
676
677         ctx->remove = false;
678         p = search_word_table(propfields, fname, ctx->mode);
679         if (!p)
680         {
681                 ereport(DEBUG1,
682                                 (errmsg("JSON parser encoutered unknown field name: \"%s\".", fname),
683                                  errdetail_log("INPUT: \"%s\"", ctx->org_string)));
684         }               
685
686         ctx->remove = (ctx->mode == PGSP_JSON_NORMALIZE &&
687                                    (!p || !p->normalize_use));
688
689         if (ctx->remove)
690                 return;
691
692         if (!bms_is_member(ctx->level, ctx->first))
693         {
694                 appendStringInfoChar(ctx->dest, ',');
695                 if (ctx->mode == PGSP_JSON_INFLATE)
696                         appendStringInfoChar(ctx->dest, '\n');
697         }
698         else
699                 ctx->first = bms_del_member(ctx->first, ctx->level);
700
701         if (ctx->mode == PGSP_JSON_INFLATE)
702                 appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);
703
704         /*
705          * We intentionally let some property names not have a short name. Use long
706          * name for the cases.
707          */
708         if (!p || !p->longname)
709                 fn = fname;
710         else if (ctx->mode == PGSP_JSON_INFLATE ||
711                          !(p->shortname && p->shortname[0]))
712                 fn = p->longname;
713         else
714                 fn = p->shortname;
715
716         escape_json(ctx->dest, fn);
717         ctx->fname = fn;
718         ctx->valconverter = (p ? p->converter : NULL);
719
720         appendStringInfoChar(ctx->dest, ':');
721
722         if (ctx->mode == PGSP_JSON_INFLATE)
723                 appendStringInfoChar(ctx->dest, ' ');
724
725         if (p && p->tag == P_GroupKeys)
726         {
727                 ctx->current_list = p->tag;
728                 ctx->list_fname = fname;
729                 ctx->wlist_level = 0;
730         }
731 }
732
733 static void
734 json_ofend(void *state, char *fname, bool isnull)
735 {
736         pgspParserContext *ctx = (pgspParserContext *)state;
737
738         if (ctx->list_fname && strcmp(fname, ctx->list_fname) == 0)
739         {
740                 ctx->list_fname = NULL;
741                 ctx->current_list = P_Invalid;
742         }
743 }
744
745 static void
746 json_aestart(void *state, bool isnull)
747 {
748         pgspParserContext *ctx = (pgspParserContext *)state;
749         if (ctx->remove)
750                 return;
751
752         if (ctx->current_list == P_GroupKeys &&
753                 ctx->wlist_level == 1)
754         {
755                 if (!bms_is_member(ctx->level, ctx->first))
756                         appendStringInfoChar(ctx->dest, ',');
757                 
758                 appendStringInfoChar(ctx->dest, '\n');
759                 appendStringInfoSpaces(ctx->dest, (ctx->level) * INDENT_STEP);
760         }
761         else
762         {
763                 if (!bms_is_member(ctx->level, ctx->first))
764                 {
765                         appendStringInfoChar(ctx->dest, ',');
766
767                         if (ctx->mode == PGSP_JSON_INFLATE &&
768                                 !ctx->last_elem_is_object)
769                                 appendStringInfoChar(ctx->dest, ' ');
770                 }
771         }
772
773         ctx->first = bms_del_member(ctx->first, ctx->level);
774 }
775
776 static void
777 json_scalar(void *state, char *token, JsonTokenType tokentype)
778 {
779         pgspParserContext *ctx = (pgspParserContext *)state;
780         const char *val = token;
781
782         if (ctx->remove)
783                 return;
784
785         if (ctx->valconverter)
786                 val = ctx->valconverter(token, ctx->mode);
787
788         if (tokentype == JSON_TOKEN_STRING)
789                 escape_json(ctx->dest, val);
790         else
791                 appendStringInfoString(ctx->dest, val);
792         ctx->last_elem_is_object = false;
793 }
794
795
796 /* YAML */
797 static void
798 yaml_objstart(void *state)
799 {
800         pgspParserContext *ctx = (pgspParserContext *)state;
801
802         if (ctx->fname)
803         {
804                 if (ctx->dest->len > 0)
805                         appendStringInfoChar(ctx->dest, '\n');
806                 appendStringInfoSpaces(ctx->dest, (ctx->level - 1) * INDENT_STEP);
807                 appendStringInfoString(ctx->dest, "- ");
808                 appendStringInfoString(ctx->dest, ctx->fname);
809                 appendStringInfoString(ctx->dest, ":\n");
810                 appendStringInfoSpaces(ctx->dest, (ctx->level + 1) * INDENT_STEP);
811                 ctx->fname = NULL;
812         }
813
814         ctx->level++;
815         ctx->first = bms_add_member(ctx->first, ctx->level);
816 }
817
818 static void
819 yaml_objend(void *state)
820 {
821         pgspParserContext *ctx = (pgspParserContext *)state;
822
823         ctx->level--;
824         ctx->last_elem_is_object = true;
825         ctx->first = bms_del_member(ctx->first, ctx->level);
826 }
827
828 static void
829 yaml_arrstart(void *state)
830 {
831         pgspParserContext *ctx = (pgspParserContext *)state;
832
833         if (ctx->fname)
834         {
835                 appendStringInfoString(ctx->dest, ctx->fname);
836                 appendStringInfoString(ctx->dest, ":");
837         }
838
839         ctx->fname = NULL;
840         ctx->level++;
841         ctx->first = bms_add_member(ctx->first, ctx->level);
842 }
843
844 static void
845 yaml_arrend(void *state)
846 {
847         pgspParserContext *ctx = (pgspParserContext *)state;
848         ctx->level--;
849 }
850 static void
851 yaml_ofstart(void *state, char *fname, bool isnull)
852 {
853         word_table *p;
854         pgspParserContext *ctx = (pgspParserContext *)state;
855         char *s;
856
857         p = search_word_table(propfields, fname, ctx->mode);
858         if (!p)
859         {
860                 ereport(DEBUG1,
861                                 (errmsg("Short JSON parser encoutered unknown field name: \"%s\".", fname),
862                                  errdetail_log("INPUT: \"%s\"", ctx->org_string)));
863         }               
864         s = (p ? p->longname : fname);
865
866         if (!bms_is_member(ctx->level, ctx->first))
867         {
868                 appendStringInfoString(ctx->dest, "\n");
869                 appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);
870         }
871         else
872                 ctx->first = bms_del_member(ctx->first, ctx->level);
873
874         ctx->valconverter = NULL;
875         ctx->fname = s;
876         ctx->valconverter = (p ? p->converter : NULL);
877 }
878
879 static void
880 yaml_aestart(void *state, bool isnull)
881 {
882         pgspParserContext *ctx = (pgspParserContext *)state;
883
884         appendStringInfoString(ctx->dest, "\n");
885         bms_del_member(ctx->first, ctx->level);
886         appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);
887         appendStringInfoString(ctx->dest, "- ");
888 }
889
890 static void
891 yaml_scalar(void *state, char *token, JsonTokenType tokentype)
892 {
893         pgspParserContext *ctx = (pgspParserContext *)state;
894
895         if (ctx->fname)
896         {
897                 appendStringInfoString(ctx->dest, ctx->fname);
898                 appendStringInfoString(ctx->dest, ": ");
899                 ctx->fname = NULL;
900         }
901
902         json_scalar(state, token, tokentype);
903
904         ctx->last_elem_is_object = false;
905 }
906
907
908 /* XML */
909 static void
910 xml_objstart(void *state)
911 {
912         pgspParserContext *ctx = (pgspParserContext *)state;
913
914         ctx->level ++;
915         ctx->first = bms_add_member(ctx->first, ctx->level);
916 }
917
918
919 static void
920 xml_objend(void *state)
921 {
922         pgspParserContext *ctx = (pgspParserContext *)state;
923         appendStringInfoChar(ctx->dest, '\n');
924         appendStringInfoSpaces(ctx->dest, ctx->level * INDENT_STEP);
925
926         ctx->level--;
927         ctx->first = bms_del_member(ctx->first, ctx->level);
928
929         ctx->last_elem_is_object = true;
930 }
931
932 static void
933 xml_arrend(void *state)
934 {
935         pgspParserContext *ctx = (pgspParserContext *)state;
936
937         appendStringInfoChar(ctx->dest, '\n');
938         appendStringInfoSpaces(ctx->dest, (ctx->level + 1) * INDENT_STEP);
939 }
940
941 static void
942 adjust_wbuf(pgspParserContext *ctx, int len)
943 {
944         int buflen;
945
946         for (buflen = ctx->wbuflen ; len > buflen ; buflen *= 2);
947         if (buflen > ctx->wbuflen)
948         {
949                 ctx->wbuf = (char *)palloc(buflen);
950                 ctx->wbuflen = buflen;
951         }
952 }
953
954 static char *
955 hyphenate_words(pgspParserContext *ctx, char *src)
956 {
957         char *p;
958
959         adjust_wbuf(ctx, strlen(src) + 1);
960         strcpy(ctx->wbuf, src);
961
962         for (p = ctx->wbuf ; *p ; p++)
963                 if (*p == ' ') *p = '-';
964
965         return ctx->wbuf;
966 }
967
968 static void
969 xml_ofstart(void *state, char *fname, bool isnull)
970 {
971         word_table *p;
972         pgspParserContext *ctx = (pgspParserContext *)state;
973         char *s;
974
975         p = search_word_table(propfields, fname, ctx->mode);
976         if (!p)
977         {
978                 ereport(DEBUG1,
979                                 (errmsg("Short JSON parser encoutered unknown field name: \"%s\".", fname),
980                                  errdetail_log("INPUT: \"%s\"", ctx->org_string)));
981         }               
982         s = (p ? p->longname : fname);
983
984         /*
985          * save current process context
986          * There's no problem if P_Plan appears recursively.
987          */
988         if (p && (p->tag == P_Plan || p->tag == P_Triggers))
989                 ctx->section = p->tag;
990
991         appendStringInfoChar(ctx->dest, '\n');
992         appendStringInfoSpaces(ctx->dest, (ctx->level + 1) * INDENT_STEP);
993
994         ctx->valconverter = NULL;
995
996         appendStringInfoChar(ctx->dest, '<');
997         appendStringInfoString(ctx->dest, escape_xml(hyphenate_words(ctx, s)));
998         appendStringInfoChar(ctx->dest, '>');
999         ctx->valconverter = (p ? p->converter : NULL);
1000
1001         /*
1002          * If the object field name is Plan or Triggers, the value should be an
1003          * array and the items are tagged by other than "Item". "Item"s appear
1004          * only in Output field.
1005          */
1006         if (p && (p->tag == P_Plans || p->tag == P_Triggers))
1007                 ctx->not_item = bms_add_member(ctx->not_item, ctx->level + 1);
1008         else
1009                 ctx->not_item = bms_del_member(ctx->not_item, ctx->level + 1);
1010 }
1011
1012 static void
1013 xml_ofend(void *state, char *fname, bool isnull)
1014 {
1015         pgspParserContext *ctx = (pgspParserContext *)state;
1016         word_table *p;
1017         char *s;
1018
1019         p =     search_word_table(propfields, fname, ctx->mode);
1020         s = (p ? p->longname : fname);
1021         
1022         appendStringInfoString(ctx->dest, "</");
1023         appendStringInfoString(ctx->dest, escape_xml(hyphenate_words(ctx, s)));
1024         appendStringInfoChar(ctx->dest, '>');
1025 }
1026
1027 static void
1028 xml_aestart(void *state, bool isnull)
1029 {
1030         pgspParserContext *ctx = (pgspParserContext *)state;
1031         char *tag;
1032
1033         /*
1034          * The "Trigger" in "Triggers", "Plan" in "Plans" and "Item" nodes are
1035          * implicitly represented in JSON format.  Restore them for XML format.
1036          */
1037
1038         ctx->level++;
1039         if (bms_is_member(ctx->level, ctx->not_item))
1040         {
1041                 if (ctx->section == P_Plan)
1042                         tag = "<Plan>";
1043                 else
1044                         tag = "<Trigger>";
1045         }
1046         else
1047                 tag = "<Item>";
1048
1049         appendStringInfoChar(ctx->dest, '\n');
1050         appendStringInfoSpaces(ctx->dest, (ctx->level + 1) * INDENT_STEP);
1051         appendStringInfoString(ctx->dest, tag);
1052 }
1053
1054 static void
1055 xml_aeend(void *state, bool isnull)
1056 {
1057         pgspParserContext *ctx = (pgspParserContext *)state;
1058         char *tag;
1059
1060         /*
1061          * The "Plan" in "Plans" or "Item" nodes are implicitly represented in
1062          * JSON format.  Restore it for XML format.
1063          */
1064
1065         if (bms_is_member(ctx->level, ctx->not_item))
1066         {
1067                 if (ctx->section == P_Plan)
1068                         tag = "</Plan>";
1069                 else
1070                         tag = "</Trigger>";
1071         }
1072         else
1073                 tag = "</Item>";
1074         appendStringInfoString(ctx->dest, tag);
1075         ctx->level--;
1076 }
1077
1078 static void
1079 xml_scalar(void *state, char *token, JsonTokenType tokentype)
1080 {
1081         pgspParserContext *ctx = (pgspParserContext *)state;
1082         const char *s = token;
1083
1084         if (ctx->valconverter)
1085                 s = ctx->valconverter(token, PGSP_JSON_XMLIZE);
1086
1087         if (tokentype == JSON_TOKEN_STRING)
1088                 s = escape_xml(s);
1089
1090         appendStringInfoString(ctx->dest, s);
1091         ctx->last_elem_is_object = false;
1092 }
1093
1094 /********************************/
1095 void
1096 init_parser_context(pgspParserContext *ctx, int mode,
1097                                            char *orgstr, char *buf, int buflen){
1098         memset(ctx, 0, sizeof(*ctx));
1099         ctx->dest = makeStringInfo();
1100         ctx->mode = mode;
1101         ctx->org_string = orgstr;
1102         ctx->wbuf = buf;
1103         ctx->wbuflen = buflen;
1104 }
1105
1106 /*
1107  * run_pg_parse_json:
1108  *
1109  * Wrap pg_parse_json in order to restore InterruptHoldoffCount when parse
1110  * error occured.
1111  *
1112  * Returns true when parse completed. False for unexpected end of string.
1113  */
1114 bool
1115 run_pg_parse_json(JsonLexContext *lex, JsonSemAction *sem)
1116 {
1117         MemoryContext ccxt = CurrentMemoryContext;
1118         uint32 saved_IntrHoldoffCount;
1119
1120         /*
1121          * "ereport(ERROR.." occurs on error in pg_parse_json resets
1122          * InterruptHoldoffCount to zero, so we must save the value before calling
1123          * json parser to restore it on parse error. See errfinish().
1124          */
1125         saved_IntrHoldoffCount = InterruptHoldoffCount;
1126
1127         PG_TRY();
1128         {
1129                 pg_parse_json(lex, sem);
1130         }
1131         PG_CATCH();
1132         {
1133                 ErrorData *errdata;
1134                 MemoryContext ecxt;
1135
1136                 InterruptHoldoffCount = saved_IntrHoldoffCount;
1137
1138                 ecxt = MemoryContextSwitchTo(ccxt);
1139                 errdata = CopyErrorData();
1140                 
1141                 if (errdata->sqlerrcode == ERRCODE_INVALID_TEXT_REPRESENTATION)
1142                 {
1143                         FlushErrorState();
1144                         return false;
1145                 }
1146                 else
1147                 {
1148                         MemoryContextSwitchTo(ecxt);
1149                         PG_RE_THROW();
1150                 }
1151         }
1152         PG_END_TRY();
1153
1154         return true;
1155 }
1156
1157 void
1158 init_json_lex_context(JsonLexContext *lex, char *json)
1159 {
1160         lex->input = lex->token_terminator = lex->line_start = json;
1161         lex->line_number = 1;
1162         lex->input_length = strlen(json);
1163         lex->strval = makeStringInfo();
1164 }
1165
1166 static void
1167 init_json_semaction(JsonSemAction *sem, pgspParserContext *ctx)
1168 {
1169         sem->semstate = (void*)ctx;
1170         sem->object_start       = json_objstart;
1171         sem->object_end         = json_objend;
1172         sem->array_start        = json_arrstart;
1173         sem->array_end          = json_arrend;
1174         sem->object_field_start = json_ofstart;
1175         sem->object_field_end   = json_ofend;
1176         sem->array_element_start= json_aestart;
1177         sem->array_element_end  = NULL;
1178         sem->scalar             = json_scalar;
1179 }
1180
1181 char *
1182 pgsp_json_shorten(char *json)
1183 {
1184         JsonLexContext lex;
1185         JsonSemAction sem;
1186         pgspParserContext    ctx;
1187
1188         init_json_lex_context(&lex, json);
1189         init_parser_context(&ctx, PGSP_JSON_SHORTEN, json, NULL, 0);
1190         init_json_semaction(&sem, &ctx);
1191
1192         run_pg_parse_json(&lex, &sem);
1193
1194         return ctx.dest->data;
1195 }
1196
1197 char *
1198 pgsp_json_normalize(char *json)
1199 {
1200         JsonLexContext lex;
1201         JsonSemAction sem;
1202         pgspParserContext    ctx;
1203
1204         init_json_lex_context(&lex, json);
1205         init_parser_context(&ctx,PGSP_JSON_NORMALIZE, json, NULL, 0);
1206         init_json_semaction(&sem, &ctx);
1207
1208         run_pg_parse_json(&lex, &sem);
1209
1210         return ctx.dest->data;
1211 }
1212
1213 char *
1214 pgsp_json_inflate(char *json)
1215 {
1216         JsonLexContext lex;
1217         JsonSemAction sem;
1218         pgspParserContext    ctx;
1219
1220         init_json_lex_context(&lex, json);
1221         init_parser_context(&ctx, PGSP_JSON_INFLATE, json, NULL, 0);
1222         init_json_semaction(&sem, &ctx);
1223
1224         if (!run_pg_parse_json(&lex, &sem))
1225         {
1226                 if (ctx.dest->len > 0 &&
1227                         ctx.dest->data[ctx.dest->len - 1] != '\n')
1228                         appendStringInfoChar(ctx.dest, '\n');
1229                 
1230                 if (ctx.dest->len == 0)
1231                         appendStringInfoString(ctx.dest, "<Input was not JSON>");
1232                 else
1233                         appendStringInfoString(ctx.dest, "<truncated>");
1234         }
1235
1236         return ctx.dest->data;
1237 }
1238
1239 char *
1240 pgsp_json_yamlize(char *json)
1241 {
1242         pgspParserContext    ctx;
1243         JsonSemAction sem;
1244         JsonLexContext lex;
1245
1246         init_json_lex_context(&lex, json);
1247         init_parser_context(&ctx, PGSP_JSON_YAMLIZE, json, NULL, 0);
1248
1249         sem.semstate = (void*)&ctx;
1250         sem.object_start       = yaml_objstart;
1251         sem.object_end         = yaml_objend;
1252         sem.array_start        = yaml_arrstart;
1253         sem.array_end          = yaml_arrend;
1254         sem.object_field_start = yaml_ofstart;
1255         sem.object_field_end   = NULL;
1256         sem.array_element_start= yaml_aestart;
1257         sem.array_element_end  = NULL;
1258         sem.scalar             = yaml_scalar;
1259
1260         if (!run_pg_parse_json(&lex, &sem))
1261         {
1262                 if (ctx.dest->len > 0 &&
1263                         ctx.dest->data[ctx.dest->len - 1] != '\n')
1264                         appendStringInfoChar(ctx.dest, '\n');
1265                 
1266                 if (ctx.dest->len == 0)
1267                         appendStringInfoString(ctx.dest, "<Input was not JSON>");
1268                 else
1269                         appendStringInfoString(ctx.dest, "<truncated>");
1270         }
1271
1272         return ctx.dest->data;
1273 }
1274
1275 char *
1276 pgsp_json_xmlize(char *json)
1277 {
1278         pgspParserContext      ctx;
1279         JsonSemAction sem;
1280         JsonLexContext lex;
1281         int start_len;
1282         char buf[32];
1283
1284         init_json_lex_context(&lex, json);
1285         init_parser_context(&ctx, PGSP_JSON_XMLIZE, json, buf, sizeof(buf));
1286
1287         sem.semstate = (void*)&ctx;
1288         sem.object_start       = xml_objstart;
1289         sem.object_end         = xml_objend;
1290         sem.array_start        = NULL;
1291         sem.array_end          = xml_arrend;
1292         sem.object_field_start = xml_ofstart;
1293         sem.object_field_end   = xml_ofend;
1294         sem.array_element_start= xml_aestart;
1295         sem.array_element_end  = xml_aeend;
1296         sem.scalar             = xml_scalar;
1297
1298         appendStringInfo(ctx.dest,
1299                                          "<explain xmlns=\"http://www.postgresql.org/2009/explain\">\n  <Query>");
1300         start_len = ctx.dest->len;
1301
1302         if (!run_pg_parse_json(&lex, &sem))
1303         {
1304                 if (ctx.dest->len > start_len &&
1305                         ctx.dest->data[ctx.dest->len - 1] != '\n')
1306                         appendStringInfoChar(ctx.dest, '\n');
1307                 
1308                 if (ctx.dest->len == start_len)
1309                 {
1310                         resetStringInfo(ctx.dest);
1311                         appendStringInfoString(ctx.dest, "<Input was not JSON>");
1312                 }
1313                 else
1314                         appendStringInfoString(ctx.dest, "<truncated>");
1315         }
1316         else
1317                 appendStringInfo(ctx.dest, "</Query>\n</explain>\n");
1318
1319         return ctx.dest->data;
1320 }