OSDN Git Service

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