pg_query 6.1.0 → 6.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -1
- data/README.md +1 -1
- data/Rakefile +3 -2
- data/ext/pg_query/extconf.rb +2 -2
- data/ext/pg_query/include/pg_query.h +32 -2
- data/ext/pg_query/include/postgres/access/amapi.h +1 -1
- data/ext/pg_query/include/postgres/access/slru.h +1 -1
- data/ext/pg_query/include/postgres/access/tableam.h +11 -4
- data/ext/pg_query/include/postgres/access/xlog.h +1 -0
- data/ext/pg_query/include/postgres/access/xlogdefs.h +2 -1
- data/ext/pg_query/include/postgres/commands/defrem.h +1 -1
- data/ext/pg_query/include/postgres/commands/trigger.h +18 -0
- data/ext/pg_query/include/postgres/executor/executor.h +4 -0
- data/ext/pg_query/include/postgres/mb/pg_wchar.h +2 -0
- data/ext/pg_query/include/postgres/miscadmin.h +2 -1
- data/ext/pg_query/include/postgres/nodes/execnodes.h +6 -8
- data/ext/pg_query/include/postgres/nodes/pathnodes.h +1 -2
- data/ext/pg_query/include/postgres/pg_config.h +10 -9
- data/ext/pg_query/include/postgres/pg_config_manual.h +2 -0
- data/ext/pg_query/include/postgres/port/atomics/generic-gcc.h +10 -2
- data/ext/pg_query/include/postgres/port/pg_iovec.h +9 -3
- data/ext/pg_query/include/postgres/replication/reorderbuffer.h +29 -9
- data/ext/pg_query/include/postgres/replication/slot.h +2 -0
- data/ext/pg_query/include/postgres/utils/elog.h +1 -0
- data/ext/pg_query/include/postgres/utils/guc.h +1 -1
- data/ext/pg_query/include/postgres/utils/guc_hooks.h +0 -2
- data/ext/pg_query/include/postgres/utils/pg_locale.h +5 -0
- data/ext/pg_query/include/postgres_deparse.h +34 -0
- data/ext/pg_query/include/protobuf/pg_query.pb-c.h +673 -516
- data/ext/pg_query/pg_query.pb-c.c +488 -0
- data/ext/pg_query/pg_query_deparse.c +148 -15
- data/ext/pg_query/pg_query_internal.h +9 -8
- data/ext/pg_query/pg_query_is_utility_stmt.c +70 -0
- data/ext/pg_query/pg_query_normalize.c +3 -0
- data/ext/pg_query/pg_query_raw_tree_walker_supports.c +117 -0
- data/ext/pg_query/pg_query_ruby.c +150 -0
- data/ext/pg_query/pg_query_summary.c +941 -0
- data/ext/pg_query/pg_query_summary.h +109 -0
- data/ext/pg_query/pg_query_summary_statement_type.c +797 -0
- data/ext/pg_query/pg_query_summary_truncate.c +530 -0
- data/ext/pg_query/postgres_deparse.c +4481 -3870
- data/ext/pg_query/src_backend_catalog_namespace.c +29 -0
- data/ext/pg_query/src_backend_nodes_bitmapset.c +84 -1
- data/ext/pg_query/src_backend_nodes_list.c +60 -1
- data/ext/pg_query/src_backend_parser_gram.c +739 -732
- data/ext/pg_query/src_backend_utils_activity_pgstat_database.c +2 -2
- data/ext/pg_query/src_backend_utils_error_elog.c +11 -0
- data/ext/pg_query/src_backend_utils_mb_mbutils.c +43 -4
- data/ext/pg_query/src_backend_utils_mmgr_alignedalloc.c +22 -7
- data/ext/pg_query/src_backend_utils_mmgr_aset.c +3 -3
- data/ext/pg_query/src_backend_utils_mmgr_bump.c +1 -1
- data/ext/pg_query/src_common_stringinfo.c +20 -0
- data/ext/pg_query/src_common_wchar.c +46 -6
- data/lib/pg_query/deparse.rb +29 -8
- data/lib/pg_query/parse.rb +19 -0
- data/lib/pg_query/pg_query_pb.rb +7 -2
- data/lib/pg_query/split.rb +20 -0
- data/lib/pg_query/treewalker.rb +9 -7
- data/lib/pg_query/version.rb +1 -1
- data/lib/pg_query.rb +1 -0
- metadata +10 -3
- data/ext/pg_query/postgres_deparse.h +0 -9
|
@@ -0,0 +1,941 @@
|
|
|
1
|
+
#include "pg_query.h"
|
|
2
|
+
#include "pg_query_internal.h"
|
|
3
|
+
#include "pg_query_outfuncs.h"
|
|
4
|
+
|
|
5
|
+
#include "catalog/namespace.h"
|
|
6
|
+
|
|
7
|
+
#include "parser/parser.h"
|
|
8
|
+
#include "parser/scanner.h"
|
|
9
|
+
#include "parser/scansup.h"
|
|
10
|
+
|
|
11
|
+
#include "nodes/makefuncs.h"
|
|
12
|
+
#include "nodes/nodeFuncs.h"
|
|
13
|
+
#include "utils/builtins.h"
|
|
14
|
+
|
|
15
|
+
#include "protobuf/pg_query.pb-c.h"
|
|
16
|
+
|
|
17
|
+
#include <unistd.h>
|
|
18
|
+
#include <fcntl.h>
|
|
19
|
+
#include <string.h>
|
|
20
|
+
|
|
21
|
+
#include "pg_query_summary.h"
|
|
22
|
+
|
|
23
|
+
static char *range_var_to_name(RangeVar *rv);
|
|
24
|
+
static bool cte_names_include(WalkState * state, char *relname);
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
static PgQueryError * make_pg_query_error(const char *message)
|
|
28
|
+
{
|
|
29
|
+
PgQueryError *error = malloc(sizeof(PgQueryError));
|
|
30
|
+
|
|
31
|
+
error->message = strdup(message);
|
|
32
|
+
return error;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* Given a Node (node) and WalkState (state), wraps node in a RangeVarWithContext
|
|
37
|
+
* and adds it to the state->range_vars list.
|
|
38
|
+
*/
|
|
39
|
+
static void
|
|
40
|
+
add_range_var(Node *node, ContextType context, WalkState * state)
|
|
41
|
+
{
|
|
42
|
+
if (!node)
|
|
43
|
+
return;
|
|
44
|
+
|
|
45
|
+
switch (node->type)
|
|
46
|
+
{
|
|
47
|
+
case T_JoinExpr:
|
|
48
|
+
{
|
|
49
|
+
JoinExpr *je = castNode(JoinExpr, node);
|
|
50
|
+
|
|
51
|
+
add_range_var(je->larg, context, state);
|
|
52
|
+
add_range_var(je->rarg, context, state);
|
|
53
|
+
/* je->quals is handled by the regular tree walk. */
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
case T_RangeTableSample:
|
|
58
|
+
{
|
|
59
|
+
RangeTableSample *rts = castNode(RangeTableSample, node);
|
|
60
|
+
|
|
61
|
+
add_range_var(rts->relation, context, state);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case T_JsonTable:
|
|
66
|
+
case T_RangeTableFunc:
|
|
67
|
+
case T_RangeFunction:
|
|
68
|
+
case T_RangeSubselect:
|
|
69
|
+
/* These are handled by the tree walker. */
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
case T_RangeVar:
|
|
73
|
+
{
|
|
74
|
+
RangeVarWithContext *rvwc = palloc(sizeof(RangeVarWithContext));
|
|
75
|
+
|
|
76
|
+
rvwc->node = castNode(RangeVar, node);
|
|
77
|
+
rvwc->context = context;
|
|
78
|
+
|
|
79
|
+
state->range_vars = lappend(state->range_vars, rvwc);
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
default:
|
|
84
|
+
elog(ERROR, "unexpected node type: %d", nodeTag(node));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* Given a List*-of-Nodes (clause) and WalkState (state), wrap them all
|
|
90
|
+
* in RangeVarWithContext structs and add them to the state->range_vars list.
|
|
91
|
+
*/
|
|
92
|
+
static void
|
|
93
|
+
add_range_var_list(List *clause, ContextType context, WalkState * state)
|
|
94
|
+
{
|
|
95
|
+
if (!clause)
|
|
96
|
+
return;
|
|
97
|
+
|
|
98
|
+
ListCell *lc = NULL;
|
|
99
|
+
|
|
100
|
+
foreach(lc, clause)
|
|
101
|
+
{
|
|
102
|
+
add_range_var(lfirst(lc), context, state);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
static void
|
|
107
|
+
add_function(List *funcname, ContextType context, WalkState * state)
|
|
108
|
+
{
|
|
109
|
+
SummaryFunction *func = palloc(sizeof(SummaryFunction));
|
|
110
|
+
|
|
111
|
+
func->name = NameListToString(funcname);
|
|
112
|
+
|
|
113
|
+
if (list_length(funcname) == 3)
|
|
114
|
+
func->schema_name = lsecond_node(String, funcname)->sval;
|
|
115
|
+
else if (list_length(funcname) == 2)
|
|
116
|
+
func->schema_name = linitial_node(String, funcname)->sval;
|
|
117
|
+
else
|
|
118
|
+
func->schema_name = NULL;
|
|
119
|
+
|
|
120
|
+
func->function_name = llast_node(String, funcname)->sval;
|
|
121
|
+
|
|
122
|
+
func->context = context;
|
|
123
|
+
|
|
124
|
+
state->functions = lappend(state->functions, func);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
bool
|
|
128
|
+
pg_query_summary_walk_impl(Node *node, WalkState * state)
|
|
129
|
+
{
|
|
130
|
+
if (node == NULL)
|
|
131
|
+
return false;
|
|
132
|
+
|
|
133
|
+
switch (nodeTag(node))
|
|
134
|
+
{
|
|
135
|
+
case T_CommonTableExpr:
|
|
136
|
+
{
|
|
137
|
+
CommonTableExpr *cte = castNode(CommonTableExpr, node);
|
|
138
|
+
|
|
139
|
+
state->cte_names = lappend(state->cte_names, pstrdup(cte->ctename));
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
case T_CallStmt:
|
|
144
|
+
{
|
|
145
|
+
CallStmt *stmt = castNode(CallStmt, node);
|
|
146
|
+
|
|
147
|
+
if (stmt->funccall)
|
|
148
|
+
return pg_query_summary_walk_impl((Node *) stmt->funccall, state);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
case T_SelectStmt:
|
|
152
|
+
{
|
|
153
|
+
/*
|
|
154
|
+
* Don't descend into sub-SELECTs in the WHERE clause when
|
|
155
|
+
* saving filter columns.
|
|
156
|
+
*/
|
|
157
|
+
if (state->save_filter_columns)
|
|
158
|
+
return true;
|
|
159
|
+
|
|
160
|
+
SelectStmt *stmt = castNode(SelectStmt, node);
|
|
161
|
+
|
|
162
|
+
if (stmt->op == SETOP_NONE)
|
|
163
|
+
add_range_var_list(stmt->fromClause, CONTEXT_SELECT, state);
|
|
164
|
+
|
|
165
|
+
/*
|
|
166
|
+
* For SETOP_UNION, SETOP_EXCEPT, and SETOP_INTERSECT, the
|
|
167
|
+
* raw_expression_tree_walker() call at the end of this
|
|
168
|
+
* function will handle stmt->larg/stmt->rarg.
|
|
169
|
+
*/
|
|
170
|
+
|
|
171
|
+
if (stmt->intoClause)
|
|
172
|
+
add_range_var((Node *) stmt->intoClause->rel, CONTEXT_DDL, state);
|
|
173
|
+
|
|
174
|
+
if (stmt->whereClause)
|
|
175
|
+
{
|
|
176
|
+
/*
|
|
177
|
+
* If we have a WHERE clause, that means we have to check
|
|
178
|
+
* for filter columns.
|
|
179
|
+
*
|
|
180
|
+
* NOTE: the WHERE clause is intentionally walked twice,
|
|
181
|
+
* but when saving filter columns we don't descend into
|
|
182
|
+
* sub-SELECTs.
|
|
183
|
+
*/
|
|
184
|
+
state->save_filter_columns = true;
|
|
185
|
+
pg_query_summary_walk_impl((Node *) stmt->whereClause, state);
|
|
186
|
+
state->save_filter_columns = false;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
case T_InsertStmt:
|
|
193
|
+
{
|
|
194
|
+
add_range_var((Node *) castNode(InsertStmt, node)->relation, CONTEXT_DML, state);
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
case T_UpdateStmt:
|
|
199
|
+
{
|
|
200
|
+
UpdateStmt *stmt = castNode(UpdateStmt, node);
|
|
201
|
+
|
|
202
|
+
add_range_var((Node *) stmt->relation, CONTEXT_DML, state);
|
|
203
|
+
add_range_var_list(stmt->fromClause, CONTEXT_SELECT, state);
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
case T_MergeStmt:
|
|
208
|
+
{
|
|
209
|
+
MergeStmt *stmt = castNode(MergeStmt, node);
|
|
210
|
+
|
|
211
|
+
add_range_var((Node *) stmt->relation, CONTEXT_DML, state);
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
case T_DeleteStmt:
|
|
216
|
+
{
|
|
217
|
+
DeleteStmt *stmt = castNode(DeleteStmt, node);
|
|
218
|
+
|
|
219
|
+
add_range_var((Node *) stmt->relation, CONTEXT_DML, state);
|
|
220
|
+
add_range_var_list(stmt->usingClause, CONTEXT_SELECT, state);
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
case T_CopyStmt:
|
|
225
|
+
{
|
|
226
|
+
CopyStmt *stmt = castNode(CopyStmt, node);
|
|
227
|
+
|
|
228
|
+
add_range_var((Node *) stmt->relation, CONTEXT_SELECT, state);
|
|
229
|
+
pg_query_summary_walk_impl((Node *) stmt->query, state);
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
case T_AlterTableStmt:
|
|
234
|
+
{
|
|
235
|
+
AlterTableStmt *stmt = castNode(AlterTableStmt, node);
|
|
236
|
+
|
|
237
|
+
/* `ALTER INDEX index_name` is ignored. */
|
|
238
|
+
if (stmt->objtype != OBJECT_INDEX)
|
|
239
|
+
add_range_var((Node *) stmt->relation, CONTEXT_DDL, state);
|
|
240
|
+
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
case T_CreateStmt:
|
|
245
|
+
add_range_var((Node *) castNode(CreateStmt, node)->relation, CONTEXT_DDL, state);
|
|
246
|
+
break;
|
|
247
|
+
|
|
248
|
+
case T_CreateTableAsStmt:
|
|
249
|
+
{
|
|
250
|
+
CreateTableAsStmt *stmt = castNode(CreateTableAsStmt, node);
|
|
251
|
+
|
|
252
|
+
if (stmt->into && stmt->into->rel)
|
|
253
|
+
add_range_var((Node *) stmt->into->rel, CONTEXT_DDL, state);
|
|
254
|
+
|
|
255
|
+
pg_query_summary_walk_impl(stmt->query, state);
|
|
256
|
+
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
case T_TruncateStmt:
|
|
261
|
+
add_range_var_list(castNode(TruncateStmt, node)->relations, CONTEXT_DDL, state);
|
|
262
|
+
break;
|
|
263
|
+
|
|
264
|
+
case T_ViewStmt:
|
|
265
|
+
{
|
|
266
|
+
ViewStmt *stmt = castNode(ViewStmt, node);
|
|
267
|
+
|
|
268
|
+
add_range_var((Node *) stmt->view, CONTEXT_DDL, state);
|
|
269
|
+
pg_query_summary_walk_impl(stmt->query, state);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
case T_IndexStmt:
|
|
274
|
+
{
|
|
275
|
+
IndexStmt *stmt = castNode(IndexStmt, node);
|
|
276
|
+
|
|
277
|
+
add_range_var((Node *) stmt->relation, CONTEXT_DDL, state);
|
|
278
|
+
|
|
279
|
+
ListCell *lc = NULL;
|
|
280
|
+
|
|
281
|
+
foreach(lc, stmt->indexParams)
|
|
282
|
+
{
|
|
283
|
+
IndexElem *index_elem = lfirst(lc);
|
|
284
|
+
|
|
285
|
+
pg_query_summary_walk_impl(index_elem->expr, state);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
pg_query_summary_walk_impl(stmt->whereClause, state);
|
|
289
|
+
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
case T_CreateTrigStmt:
|
|
294
|
+
add_range_var((Node *) castNode(CreateTrigStmt, node)->relation, CONTEXT_DDL, state);
|
|
295
|
+
break;
|
|
296
|
+
|
|
297
|
+
case T_RuleStmt:
|
|
298
|
+
add_range_var((Node *) castNode(RuleStmt, node)->relation, CONTEXT_DDL, state);
|
|
299
|
+
break;
|
|
300
|
+
|
|
301
|
+
case T_VacuumStmt:
|
|
302
|
+
{
|
|
303
|
+
VacuumStmt *stmt = castNode(VacuumStmt, node);
|
|
304
|
+
|
|
305
|
+
ListCell *lc = NULL;
|
|
306
|
+
|
|
307
|
+
foreach(lc, stmt->rels)
|
|
308
|
+
{
|
|
309
|
+
VacuumRelation *rel = lfirst(lc);
|
|
310
|
+
|
|
311
|
+
add_range_var((Node *) rel->relation, CONTEXT_DDL, state);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
case T_RefreshMatViewStmt:
|
|
318
|
+
add_range_var((Node *) castNode(RefreshMatViewStmt, node)->relation, CONTEXT_DDL, state);
|
|
319
|
+
break;
|
|
320
|
+
|
|
321
|
+
case T_DropStmt:
|
|
322
|
+
{
|
|
323
|
+
DropStmt *stmt = castNode(DropStmt, node);
|
|
324
|
+
|
|
325
|
+
switch (stmt->removeType)
|
|
326
|
+
{
|
|
327
|
+
case OBJECT_TABLE:
|
|
328
|
+
{
|
|
329
|
+
ListCell *lc = NULL;
|
|
330
|
+
|
|
331
|
+
foreach(lc, stmt->objects)
|
|
332
|
+
{
|
|
333
|
+
Node *obj = lfirst(lc);
|
|
334
|
+
|
|
335
|
+
if (obj->type == T_List)
|
|
336
|
+
{
|
|
337
|
+
List *list = castNode(List, obj);
|
|
338
|
+
|
|
339
|
+
if (list_length(list) == 0)
|
|
340
|
+
continue;
|
|
341
|
+
|
|
342
|
+
RangeVar *range_var = makeRangeVarFromNameList(list);
|
|
343
|
+
|
|
344
|
+
add_range_var((Node *) range_var, CONTEXT_DDL, state);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
case OBJECT_RULE:
|
|
350
|
+
case OBJECT_TRIGGER:
|
|
351
|
+
{
|
|
352
|
+
ListCell *lc = NULL;
|
|
353
|
+
|
|
354
|
+
foreach(lc, stmt->objects)
|
|
355
|
+
{
|
|
356
|
+
Node *obj = lfirst(lc);
|
|
357
|
+
|
|
358
|
+
if (obj->type == T_List)
|
|
359
|
+
{
|
|
360
|
+
List *olist = castNode(List, obj);
|
|
361
|
+
|
|
362
|
+
/*
|
|
363
|
+
* Ignore the last string (the
|
|
364
|
+
* rule/trigger name)
|
|
365
|
+
*/
|
|
366
|
+
List *list = list_copy(olist);
|
|
367
|
+
|
|
368
|
+
list = list_truncate(list, list_length(list) - 1);
|
|
369
|
+
|
|
370
|
+
if (list_length(list) == 0)
|
|
371
|
+
continue;
|
|
372
|
+
|
|
373
|
+
RangeVar *range_var = makeRangeVarFromNameList(list);
|
|
374
|
+
|
|
375
|
+
add_range_var((Node *) range_var, CONTEXT_DDL, state);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
case OBJECT_FUNCTION:
|
|
382
|
+
{
|
|
383
|
+
Node *obj_node = linitial(stmt->objects);
|
|
384
|
+
|
|
385
|
+
if (obj_node != NULL && obj_node->type == T_ObjectWithArgs)
|
|
386
|
+
{
|
|
387
|
+
ObjectWithArgs *obj = castNode(ObjectWithArgs, obj_node);
|
|
388
|
+
|
|
389
|
+
if (obj->objname)
|
|
390
|
+
add_function(obj->objname, CONTEXT_DDL, state);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
default:
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
case T_GrantStmt:
|
|
403
|
+
{
|
|
404
|
+
GrantStmt *stmt = castNode(GrantStmt, node);
|
|
405
|
+
List *objects = stmt->objects;
|
|
406
|
+
|
|
407
|
+
switch (stmt->objtype)
|
|
408
|
+
{
|
|
409
|
+
case OBJECT_TABLE:
|
|
410
|
+
{
|
|
411
|
+
ListCell *lc = NULL;
|
|
412
|
+
|
|
413
|
+
foreach(lc, stmt->objects)
|
|
414
|
+
{
|
|
415
|
+
if (IsA(lfirst(lc), RangeVar))
|
|
416
|
+
add_range_var(lfirst(lc), CONTEXT_DDL, state);
|
|
417
|
+
}
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
case OBJECT_FUNCTION:
|
|
421
|
+
case OBJECT_PROCEDURE:
|
|
422
|
+
case OBJECT_ROUTINE:
|
|
423
|
+
{
|
|
424
|
+
ListCell *lc = NULL;
|
|
425
|
+
|
|
426
|
+
foreach(lc, stmt->objects)
|
|
427
|
+
{
|
|
428
|
+
Node *obj_node = lfirst(lc);
|
|
429
|
+
|
|
430
|
+
if (IsA(obj_node, ObjectWithArgs))
|
|
431
|
+
{
|
|
432
|
+
ObjectWithArgs *obj = castNode(ObjectWithArgs, obj_node);
|
|
433
|
+
|
|
434
|
+
if (obj->objname)
|
|
435
|
+
add_function(obj->objname, CONTEXT_DDL, state);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
default:
|
|
440
|
+
|
|
441
|
+
/*
|
|
442
|
+
* Do nothing.
|
|
443
|
+
*
|
|
444
|
+
* Other grant types don't contain table/function
|
|
445
|
+
* references.
|
|
446
|
+
*/
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
break;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
case T_LockStmt:
|
|
453
|
+
add_range_var_list(castNode(LockStmt, node)->relations, CONTEXT_DDL, state);
|
|
454
|
+
break;
|
|
455
|
+
|
|
456
|
+
case T_ExplainStmt:
|
|
457
|
+
return pg_query_summary_walk_impl(castNode(ExplainStmt, node)->query, state);
|
|
458
|
+
|
|
459
|
+
case T_CreateFunctionStmt:
|
|
460
|
+
add_function(castNode(CreateFunctionStmt, node)->funcname, CONTEXT_DDL, state);
|
|
461
|
+
break;
|
|
462
|
+
|
|
463
|
+
case T_RenameStmt:
|
|
464
|
+
{
|
|
465
|
+
RenameStmt *stmt = castNode(RenameStmt, node);
|
|
466
|
+
|
|
467
|
+
switch (stmt->renameType)
|
|
468
|
+
{
|
|
469
|
+
case OBJECT_TABLE:
|
|
470
|
+
add_range_var((Node *) stmt->relation, CONTEXT_DDL, state);
|
|
471
|
+
add_range_var((Node *) makeRangeVar(stmt->relation->schemaname, stmt->newname, -1), CONTEXT_DDL, state);
|
|
472
|
+
break;
|
|
473
|
+
case OBJECT_FUNCTION:
|
|
474
|
+
case OBJECT_PROCEDURE:
|
|
475
|
+
case OBJECT_ROUTINE:
|
|
476
|
+
{
|
|
477
|
+
ObjectWithArgs *obj = castNode(ObjectWithArgs, stmt->object);
|
|
478
|
+
|
|
479
|
+
add_function(obj->objname, CONTEXT_DDL, state);
|
|
480
|
+
add_function(list_make1(makeString(stmt->newname)), CONTEXT_DDL, state);
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
default:
|
|
484
|
+
|
|
485
|
+
/*
|
|
486
|
+
* Do nothing.
|
|
487
|
+
*
|
|
488
|
+
* Other rename types don't contain table/function
|
|
489
|
+
* references.
|
|
490
|
+
*/
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
case T_PrepareStmt:
|
|
497
|
+
return pg_query_summary_walk_impl(castNode(PrepareStmt, node)->query, state);
|
|
498
|
+
|
|
499
|
+
case T_RawStmt:
|
|
500
|
+
return pg_query_summary_walk_impl(castNode(RawStmt, node)->stmt, state);
|
|
501
|
+
|
|
502
|
+
case T_FuncCall:
|
|
503
|
+
add_function(castNode(FuncCall, node)->funcname, CONTEXT_CALL, state);
|
|
504
|
+
break;
|
|
505
|
+
|
|
506
|
+
/*
|
|
507
|
+
* The following are added to avoid needing subselect_items,
|
|
508
|
+
*
|
|
509
|
+
* like is used in Rust and Ruby.
|
|
510
|
+
*/
|
|
511
|
+
case T_ColumnRef:
|
|
512
|
+
{
|
|
513
|
+
if (!state->save_filter_columns)
|
|
514
|
+
break;
|
|
515
|
+
|
|
516
|
+
ColumnRef *ref = castNode(ColumnRef, node);
|
|
517
|
+
|
|
518
|
+
Node *column_node = llast(ref->fields);
|
|
519
|
+
|
|
520
|
+
/*
|
|
521
|
+
* Check that we are not in the A_Star case here. (Columns can
|
|
522
|
+
* be stars, but nothing else can.)
|
|
523
|
+
*/
|
|
524
|
+
if (!IsA(column_node, String))
|
|
525
|
+
break;
|
|
526
|
+
|
|
527
|
+
String *schema_name = NULL;
|
|
528
|
+
String *table_name = NULL;
|
|
529
|
+
String *column = castNode(String, column_node);
|
|
530
|
+
|
|
531
|
+
size_t num_fields = list_length(ref->fields);
|
|
532
|
+
|
|
533
|
+
if (num_fields == 2)
|
|
534
|
+
{
|
|
535
|
+
table_name = linitial(ref->fields);
|
|
536
|
+
}
|
|
537
|
+
else if (num_fields == 3)
|
|
538
|
+
{
|
|
539
|
+
schema_name = linitial(ref->fields);
|
|
540
|
+
table_name = lsecond_node(String, ref->fields);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
FilterColumn *fc = palloc(sizeof(FilterColumn));
|
|
544
|
+
|
|
545
|
+
fc->schema_name = schema_name ? schema_name->sval : NULL;
|
|
546
|
+
fc->table_name = table_name ? table_name->sval : NULL;
|
|
547
|
+
fc->column = column ? column->sval : NULL;
|
|
548
|
+
|
|
549
|
+
state->filter_columns = lappend(state->filter_columns, fc);
|
|
550
|
+
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
default:
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (!pg_query_raw_tree_walker_supports(node))
|
|
559
|
+
return false;
|
|
560
|
+
|
|
561
|
+
return raw_expression_tree_walker(node, pg_query_summary_walk_impl, (void *) state);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
static char *
|
|
565
|
+
range_var_to_name(RangeVar *rv)
|
|
566
|
+
{
|
|
567
|
+
StringInfoData buf;
|
|
568
|
+
|
|
569
|
+
initStringInfo(&buf);
|
|
570
|
+
if (rv->schemaname != NULL)
|
|
571
|
+
{
|
|
572
|
+
appendStringInfoString(&buf, quote_identifier(rv->schemaname));
|
|
573
|
+
appendStringInfoChar(&buf, '.');
|
|
574
|
+
}
|
|
575
|
+
appendStringInfoString(&buf, quote_identifier(rv->relname));
|
|
576
|
+
return buf.data;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/*
|
|
580
|
+
* Walk through all collected cte_names, and compares them to relname.
|
|
581
|
+
*
|
|
582
|
+
* Returns true if any are an exact match, false otherwise.
|
|
583
|
+
*/
|
|
584
|
+
static bool
|
|
585
|
+
cte_names_include(WalkState * state, char *relname)
|
|
586
|
+
{
|
|
587
|
+
if (relname == NULL)
|
|
588
|
+
return false;
|
|
589
|
+
|
|
590
|
+
ListCell *lc = NULL;
|
|
591
|
+
|
|
592
|
+
foreach(lc, state->cte_names)
|
|
593
|
+
{
|
|
594
|
+
char *ctename = lfirst(lc);
|
|
595
|
+
|
|
596
|
+
if (strcmp(ctename, relname) == 0)
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
static void
|
|
603
|
+
handle_range_var(RangeVar *node, ContextType context, WalkState * state)
|
|
604
|
+
{
|
|
605
|
+
if (node == NULL)
|
|
606
|
+
return;
|
|
607
|
+
|
|
608
|
+
RangeVar *rv = node;
|
|
609
|
+
|
|
610
|
+
if (!cte_names_include(state, rv->relname))
|
|
611
|
+
{
|
|
612
|
+
SummaryTable *table = palloc(sizeof(SummaryTable));
|
|
613
|
+
|
|
614
|
+
table->name = range_var_to_name(rv);
|
|
615
|
+
table->schema_name = pstrdup(rv->schemaname ? rv->schemaname : "");
|
|
616
|
+
table->table_name = pstrdup(rv->relname ? rv->relname : "");
|
|
617
|
+
table->context = context;
|
|
618
|
+
|
|
619
|
+
state->tables = lappend(state->tables, table);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (rv->alias)
|
|
623
|
+
{
|
|
624
|
+
SummaryAlias *alias = palloc(sizeof(SummaryAlias));
|
|
625
|
+
|
|
626
|
+
alias->key = rv->alias->aliasname;
|
|
627
|
+
alias->value = range_var_to_name(rv);
|
|
628
|
+
state->aliases = lappend(state->aliases, alias);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/* Walk the parse tree, storing the results in `summary`. */
|
|
633
|
+
static void
|
|
634
|
+
pg_query_summary_walk(Summary * summary, Node *tree)
|
|
635
|
+
{
|
|
636
|
+
WalkState state = {0};
|
|
637
|
+
|
|
638
|
+
/*
|
|
639
|
+
* NOTE regarding cte_names and range_vars:
|
|
640
|
+
* - We process cte_names as we iterate through the tree.
|
|
641
|
+
* - We only add items to tables if they are *not* in cte_names.
|
|
642
|
+
* - CTEs can be defined *after* they're referenced as names.
|
|
643
|
+
* - Due to this, we store range_vars and process it after the tree walk.
|
|
644
|
+
*
|
|
645
|
+
* If we try to do it entirely on the initial pass, we wind up with CTEs
|
|
646
|
+
* being included in state->tables, and thus in summary->tables.
|
|
647
|
+
*/
|
|
648
|
+
|
|
649
|
+
pg_query_summary_walk_impl(tree, &state);
|
|
650
|
+
|
|
651
|
+
ListCell *lc = NULL;
|
|
652
|
+
|
|
653
|
+
foreach(lc, state.range_vars)
|
|
654
|
+
{
|
|
655
|
+
RangeVarWithContext *rvwc = lfirst(lc);
|
|
656
|
+
|
|
657
|
+
handle_range_var(rvwc->node, rvwc->context, &state);
|
|
658
|
+
|
|
659
|
+
pfree(rvwc);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
if (state.tables != NULL)
|
|
663
|
+
summary->tables = state.tables;
|
|
664
|
+
|
|
665
|
+
if (state.aliases != NULL)
|
|
666
|
+
summary->aliases = state.aliases;
|
|
667
|
+
|
|
668
|
+
if (state.cte_names != NULL)
|
|
669
|
+
summary->cte_names = state.cte_names;
|
|
670
|
+
|
|
671
|
+
if (state.functions != NULL)
|
|
672
|
+
summary->functions = state.functions;
|
|
673
|
+
|
|
674
|
+
if (state.filter_columns != NULL)
|
|
675
|
+
summary->filter_columns = state.filter_columns;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
static PgQuery__SummaryResult__Context ctx_to_protobuf_ctx(ContextType ctx)
|
|
679
|
+
{
|
|
680
|
+
/*
|
|
681
|
+
* We're converting from an enum we control (ContextType) to an enum which
|
|
682
|
+
* is auto-generated by protobuf (PgQuery__SummaryResult__Context).
|
|
683
|
+
*
|
|
684
|
+
* While these should always be equivalent, we can't guarantee correctness
|
|
685
|
+
* with a simple cast. (It is theoretically possible for the internal
|
|
686
|
+
* representations to diverge while being functionally equivalent.)
|
|
687
|
+
*
|
|
688
|
+
* So, instead, we do a switch/case and manually convert it.
|
|
689
|
+
*/
|
|
690
|
+
switch (ctx)
|
|
691
|
+
{
|
|
692
|
+
case CONTEXT_NONE:
|
|
693
|
+
return PG_QUERY__SUMMARY_RESULT__CONTEXT__None;
|
|
694
|
+
case CONTEXT_SELECT:
|
|
695
|
+
return PG_QUERY__SUMMARY_RESULT__CONTEXT__Select;
|
|
696
|
+
case CONTEXT_DML:
|
|
697
|
+
return PG_QUERY__SUMMARY_RESULT__CONTEXT__DML;
|
|
698
|
+
case CONTEXT_DDL:
|
|
699
|
+
return PG_QUERY__SUMMARY_RESULT__CONTEXT__DDL;
|
|
700
|
+
case CONTEXT_CALL:
|
|
701
|
+
return PG_QUERY__SUMMARY_RESULT__CONTEXT__Call;
|
|
702
|
+
default:
|
|
703
|
+
return PG_QUERY__SUMMARY_RESULT__CONTEXT__None;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/* Given the summarized query, convert it to protobuf, and store it in `result`. */
|
|
708
|
+
static void
|
|
709
|
+
summary_to_protobuf(PgQuerySummaryParseResult * result, Summary * summary)
|
|
710
|
+
{
|
|
711
|
+
PgQueryProtobuf protobuf;
|
|
712
|
+
PgQuery__SummaryResult sr = PG_QUERY__SUMMARY_RESULT__INIT;
|
|
713
|
+
|
|
714
|
+
sr.n_tables = list_length(summary->tables);
|
|
715
|
+
sr.tables = palloc(sizeof(PgQuery__SummaryResult__Table *) * sr.n_tables);
|
|
716
|
+
|
|
717
|
+
ListCell *lc = NULL;
|
|
718
|
+
|
|
719
|
+
foreach(lc, summary->tables)
|
|
720
|
+
{
|
|
721
|
+
size_t i = foreach_current_index(lc);
|
|
722
|
+
SummaryTable *table = lfirst(lc);
|
|
723
|
+
|
|
724
|
+
sr.tables[i] = palloc(sizeof(PgQuery__SummaryResult__Table));
|
|
725
|
+
pg_query__summary_result__table__init(sr.tables[i]);
|
|
726
|
+
sr.tables[i]->name = table->name;
|
|
727
|
+
sr.tables[i]->schema_name = table->schema_name;
|
|
728
|
+
sr.tables[i]->table_name = table->table_name;
|
|
729
|
+
sr.tables[i]->context = ctx_to_protobuf_ctx(table->context);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
sr.n_aliases = list_length(summary->aliases);
|
|
733
|
+
sr.aliases = palloc(sizeof(PgQuery__SummaryResult__AliasesEntry *) * sr.n_aliases);
|
|
734
|
+
foreach(lc, summary->aliases)
|
|
735
|
+
{
|
|
736
|
+
size_t i = foreach_current_index(lc);
|
|
737
|
+
SummaryAlias *alias = lfirst(lc);
|
|
738
|
+
|
|
739
|
+
sr.aliases[i] = palloc(sizeof(PgQuery__SummaryResult__AliasesEntry));
|
|
740
|
+
pg_query__summary_result__aliases_entry__init(sr.aliases[i]);
|
|
741
|
+
sr.aliases[i]->key = alias->key;
|
|
742
|
+
sr.aliases[i]->value = alias->value;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
sr.n_cte_names = list_length(summary->cte_names);
|
|
746
|
+
sr.cte_names = palloc(sizeof(char *) * sr.n_cte_names);
|
|
747
|
+
foreach(lc, summary->cte_names)
|
|
748
|
+
{
|
|
749
|
+
size_t i = foreach_current_index(lc);
|
|
750
|
+
char *ctename = lfirst(lc);
|
|
751
|
+
|
|
752
|
+
sr.cte_names[i] = ctename;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
sr.n_functions = list_length(summary->functions);
|
|
756
|
+
sr.functions = palloc(sizeof(PgQuery__SummaryResult__Function *) * sr.n_functions);
|
|
757
|
+
foreach(lc, summary->functions)
|
|
758
|
+
{
|
|
759
|
+
size_t i = foreach_current_index(lc);
|
|
760
|
+
SummaryFunction *fn = lfirst(lc);
|
|
761
|
+
|
|
762
|
+
sr.functions[i] = palloc(sizeof(PgQuery__SummaryResult__Function));
|
|
763
|
+
pg_query__summary_result__function__init(sr.functions[i]);
|
|
764
|
+
sr.functions[i]->name = fn->name;
|
|
765
|
+
sr.functions[i]->function_name = fn->function_name;
|
|
766
|
+
sr.functions[i]->schema_name = fn->schema_name;
|
|
767
|
+
sr.functions[i]->context = ctx_to_protobuf_ctx(fn->context);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
sr.n_filter_columns = list_length(summary->filter_columns);
|
|
771
|
+
sr.filter_columns = palloc(sizeof(PgQuery__SummaryResult__FilterColumn *) * sr.n_filter_columns);
|
|
772
|
+
foreach(lc, summary->filter_columns)
|
|
773
|
+
{
|
|
774
|
+
size_t i = foreach_current_index(lc);
|
|
775
|
+
FilterColumn *fc = lfirst(lc);
|
|
776
|
+
|
|
777
|
+
sr.filter_columns[i] = palloc(sizeof(PgQuery__SummaryResult__FilterColumn));
|
|
778
|
+
pg_query__summary_result__filter_column__init(sr.filter_columns[i]);
|
|
779
|
+
sr.filter_columns[i]->schema_name = fc->schema_name;
|
|
780
|
+
sr.filter_columns[i]->table_name = fc->table_name;
|
|
781
|
+
sr.filter_columns[i]->column = fc->column;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
sr.n_statement_types = list_length(summary->statement_types);
|
|
785
|
+
sr.statement_types = palloc(sizeof(char *) * sr.n_statement_types);
|
|
786
|
+
foreach(lc, summary->statement_types)
|
|
787
|
+
{
|
|
788
|
+
size_t i = foreach_current_index(lc);
|
|
789
|
+
char *st = lfirst(lc);
|
|
790
|
+
|
|
791
|
+
sr.statement_types[i] = pstrdup(st);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (summary->truncated_query == NULL)
|
|
795
|
+
sr.truncated_query = pstrdup("");
|
|
796
|
+
else
|
|
797
|
+
sr.truncated_query = summary->truncated_query;
|
|
798
|
+
|
|
799
|
+
size_t len = pg_query__summary_result__get_packed_size(&sr);
|
|
800
|
+
|
|
801
|
+
/*
|
|
802
|
+
* Note: This is intentionally malloc so exiting the memory context
|
|
803
|
+
* doesn't free this
|
|
804
|
+
*/
|
|
805
|
+
char *data = malloc(sizeof(char) * len);
|
|
806
|
+
|
|
807
|
+
size_t written = pg_query__summary_result__pack(&sr, (void *) data);
|
|
808
|
+
|
|
809
|
+
if (written == len)
|
|
810
|
+
{
|
|
811
|
+
result->summary.len = len;
|
|
812
|
+
result->summary.data = data;
|
|
813
|
+
}
|
|
814
|
+
else
|
|
815
|
+
{
|
|
816
|
+
result->error = make_pg_query_error("summary_to_protobuf() wrote wrong amount of data?");
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
for (size_t i = 0; i < sr.n_tables; i++)
|
|
820
|
+
pfree(sr.tables[i]);
|
|
821
|
+
pfree(sr.tables);
|
|
822
|
+
|
|
823
|
+
for (size_t i = 0; i < sr.n_aliases; i++)
|
|
824
|
+
pfree(sr.aliases[i]);
|
|
825
|
+
pfree(sr.aliases);
|
|
826
|
+
|
|
827
|
+
for (size_t i = 0; i < sr.n_functions; i++)
|
|
828
|
+
pfree(sr.functions[i]);
|
|
829
|
+
pfree(sr.functions);
|
|
830
|
+
|
|
831
|
+
for (size_t i = 0; i < sr.n_filter_columns; i++)
|
|
832
|
+
pfree(sr.filter_columns[i]);
|
|
833
|
+
pfree(sr.filter_columns);
|
|
834
|
+
|
|
835
|
+
pfree(sr.truncated_query);
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/*
|
|
839
|
+
* Given a query, parser options, and truncate limit, provide a summary of the query.
|
|
840
|
+
*
|
|
841
|
+
* If `truncate_limit` is -1, no truncation is performed.
|
|
842
|
+
*/
|
|
843
|
+
PgQuerySummaryParseResultInternal
|
|
844
|
+
pg_query_summary_internal(const char *input, int parser_options, int truncate_limit)
|
|
845
|
+
{
|
|
846
|
+
PgQueryInternalParsetreeAndError parsetree_and_error;
|
|
847
|
+
PgQuerySummaryParseResultInternal result = {0};
|
|
848
|
+
Summary summary = {0};
|
|
849
|
+
PgQueryError *error = NULL;
|
|
850
|
+
bool should_truncate = (truncate_limit != -1);
|
|
851
|
+
|
|
852
|
+
parsetree_and_error = pg_query_raw_parse(input, parser_options);
|
|
853
|
+
result.stderr_buffer = parsetree_and_error.stderr_buffer;
|
|
854
|
+
result.error = parsetree_and_error.error;
|
|
855
|
+
|
|
856
|
+
if (result.error == NULL)
|
|
857
|
+
{
|
|
858
|
+
pg_query_summary_walk(&summary, (Node *) parsetree_and_error.tree);
|
|
859
|
+
pg_query_summary_statement_walk(&summary, (Node *) parsetree_and_error.tree);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (result.error == NULL && should_truncate)
|
|
863
|
+
pg_query_summary_truncate(&summary, (Node *) parsetree_and_error.tree, truncate_limit);
|
|
864
|
+
|
|
865
|
+
result.summary = summary;
|
|
866
|
+
|
|
867
|
+
return result;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
PgQuerySummaryParseResult
|
|
871
|
+
pg_query_summary(const char *input, int parser_options, int truncate_limit)
|
|
872
|
+
{
|
|
873
|
+
PgQuerySummaryParseResult result = {0};
|
|
874
|
+
MemoryContext ctx = pg_query_enter_memory_context();
|
|
875
|
+
|
|
876
|
+
PG_TRY();
|
|
877
|
+
{
|
|
878
|
+
PgQuerySummaryParseResultInternal internal_result =
|
|
879
|
+
pg_query_summary_internal(input, parser_options, truncate_limit);
|
|
880
|
+
|
|
881
|
+
result.stderr_buffer = internal_result.stderr_buffer;
|
|
882
|
+
result.error = internal_result.error;
|
|
883
|
+
|
|
884
|
+
if (result.error == NULL)
|
|
885
|
+
summary_to_protobuf(&result, &internal_result.summary);
|
|
886
|
+
}
|
|
887
|
+
PG_CATCH();
|
|
888
|
+
{
|
|
889
|
+
ErrorData *error_data;
|
|
890
|
+
PgQueryError *error;
|
|
891
|
+
|
|
892
|
+
MemoryContextSwitchTo(ctx);
|
|
893
|
+
error_data = CopyErrorData();
|
|
894
|
+
|
|
895
|
+
/*
|
|
896
|
+
* Note: This is intentionally malloc so exiting the memory context
|
|
897
|
+
* doesn't free this
|
|
898
|
+
*/
|
|
899
|
+
error = malloc(sizeof(PgQueryError));
|
|
900
|
+
error->message = strdup(error_data->message);
|
|
901
|
+
error->filename = strdup(error_data->filename);
|
|
902
|
+
error->funcname = strdup(error_data->funcname);
|
|
903
|
+
error->context = NULL;
|
|
904
|
+
error->lineno = error_data->lineno;
|
|
905
|
+
error->cursorpos = error_data->cursorpos;
|
|
906
|
+
|
|
907
|
+
result.error = error;
|
|
908
|
+
FlushErrorState();
|
|
909
|
+
}
|
|
910
|
+
PG_END_TRY();
|
|
911
|
+
|
|
912
|
+
pg_query_exit_memory_context(ctx);
|
|
913
|
+
|
|
914
|
+
return result;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
/* Free all of the various parts of PgQuerySummaryParseResultInternal. */
|
|
918
|
+
void
|
|
919
|
+
pg_query_free_summary_parse_result_internal(PgQuerySummaryParseResultInternal result)
|
|
920
|
+
{
|
|
921
|
+
if (result.error)
|
|
922
|
+
pg_query_free_error(result.error);
|
|
923
|
+
|
|
924
|
+
if (result.stderr_buffer)
|
|
925
|
+
free(result.stderr_buffer);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/* Free all of the various parts of a PgQuerySummaryParseResult. */
|
|
929
|
+
void
|
|
930
|
+
pg_query_free_summary_parse_result(PgQuerySummaryParseResult result)
|
|
931
|
+
{
|
|
932
|
+
if (result.error)
|
|
933
|
+
pg_query_free_error(result.error);
|
|
934
|
+
|
|
935
|
+
if (result.summary.data)
|
|
936
|
+
free(result.summary.data);
|
|
937
|
+
|
|
938
|
+
/* This is allocated by strdup(), so it uses malloc() instead of palloc(). */
|
|
939
|
+
if (result.stderr_buffer)
|
|
940
|
+
free(result.stderr_buffer);
|
|
941
|
+
}
|