pg_query 2.1.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/Rakefile +2 -2
- data/ext/pg_query/include/pg_config.h +3 -0
- data/ext/pg_query/pg_query_fingerprint.c +15 -0
- data/ext/pg_query/pg_query_fingerprint.h +3 -1
- data/ext/pg_query/pg_query_normalize.c +119 -12
- data/ext/pg_query/pg_query_parse_plpgsql.c +21 -1
- data/ext/pg_query/pg_query_ruby.sym +1 -0
- data/ext/pg_query/src_pl_plpgsql_src_pl_gram.c +1 -1
- data/lib/pg_query/parse.rb +43 -43
- data/lib/pg_query/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4db477ee95910b6c5d8e6c4e5f86c64cc985c0812cc461c017f652754848be1
|
4
|
+
data.tar.gz: 1167f0524fe8a23b19ee1beef4400cf5f0f817ba64f7d1893ac3d8ddb36ad0ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40d3c6157fc68c041f04bf9adcd6a6b018a821c45e877729ee1f657d97a1cea7d9b686ca2802c4dc56a21daae370433d8c96efb36f95a2a2a31f251307536c3d
|
7
|
+
data.tar.gz: 1ee0b3527a02b0e81c9177b37de943b51fe6b0f5ded846d298a659061133b3b76d25b5f76059dc5568484bca6939421b3e7d8c05e94d95f3f4a10d493a2301d6
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,32 @@
|
|
4
4
|
|
5
5
|
* ...
|
6
6
|
|
7
|
+
## 2.1.1 2021-10-13
|
8
|
+
|
9
|
+
* Update to libpg_query 13-2.1.0 ([#230](https://github.com/pganalyze/pg_query/pull/230))
|
10
|
+
- Normalize: add funcname error object
|
11
|
+
- Normalize: Match GROUP BY against target list and re-use param refs
|
12
|
+
- PL/pgSQL: Setup namespace items for parameters, support RECORD types
|
13
|
+
- This significantly improves parsing for PL/pgSQL functions, to the extent
|
14
|
+
that most functions should now parse successfully
|
15
|
+
- Normalize: Don't modify constants in TypeName typmods/arrayBounds fields
|
16
|
+
- This matches how pg_stat_statement behaves, and avoids causing parsing
|
17
|
+
errors on the normalized statement
|
18
|
+
- Don't fail builds on systems that have strchrnul support (FreeBSD)
|
19
|
+
* Fix build on FreeBSD ([#222](https://github.com/pganalyze/pg_query/pull/222))
|
20
|
+
* Add workaround for Ruby garbage collection bug ([#227](https://github.com/pganalyze/pg_query/pull/227))
|
21
|
+
- The Ruby interpreter has a bug in `String#concat` where the appended
|
22
|
+
array may be garbage collected prematurely because the compiler
|
23
|
+
optimized out a Ruby stack variable. We now call `to_ary` on the
|
24
|
+
Protobuf object to ensure the array lands on the Ruby stack so the
|
25
|
+
garbage collector sees it.
|
26
|
+
- The real fix in the interpreter is described in
|
27
|
+
https://bugs.ruby-lang.org/issues/18140#note-2, but most current Ruby
|
28
|
+
interpreters won't have this fix for some time.
|
29
|
+
* Table/function extraction: Support subselects and LATERAL better ([#229](https://github.com/pganalyze/pg_query/pull/229))
|
30
|
+
- This reworks the parsing logic so we don't ignore certain kinds of
|
31
|
+
subselects.
|
32
|
+
|
7
33
|
|
8
34
|
## 2.1.0 2021-07-04
|
9
35
|
|
data/Rakefile
CHANGED
@@ -5,8 +5,8 @@ require 'rspec/core/rake_task'
|
|
5
5
|
require 'rubocop/rake_task'
|
6
6
|
require 'open-uri'
|
7
7
|
|
8
|
-
LIB_PG_QUERY_TAG = '13-2.0
|
9
|
-
LIB_PG_QUERY_SHA256SUM = '
|
8
|
+
LIB_PG_QUERY_TAG = '13-2.1.0'.freeze
|
9
|
+
LIB_PG_QUERY_SHA256SUM = 'a01329ae5bac19b10b8ddf8012bd663a20f85f180d6d7b900c1a1ca8444d19a5'.freeze
|
10
10
|
|
11
11
|
Rake::ExtensionTask.new 'pg_query' do |ext|
|
12
12
|
ext.lib_dir = 'lib/pg_query'
|
@@ -291,6 +291,21 @@ _fingerprintNode(FingerprintContext *ctx, const void *obj, const void *parent, c
|
|
291
291
|
}
|
292
292
|
}
|
293
293
|
|
294
|
+
uint64_t pg_query_fingerprint_node(const void *node)
|
295
|
+
{
|
296
|
+
FingerprintContext ctx;
|
297
|
+
uint64 result;
|
298
|
+
|
299
|
+
_fingerprintInitContext(&ctx, NULL, false);
|
300
|
+
_fingerprintNode(&ctx, node, NULL, NULL, 0);
|
301
|
+
|
302
|
+
result = XXH3_64bits_digest(ctx.xxh_state);
|
303
|
+
|
304
|
+
_fingerprintFreeContext(&ctx);
|
305
|
+
|
306
|
+
return result;
|
307
|
+
}
|
308
|
+
|
294
309
|
PgQueryFingerprintResult pg_query_fingerprint_with_opts(const char* input, bool printTokens)
|
295
310
|
{
|
296
311
|
MemoryContext ctx = NULL;
|
@@ -3,6 +3,8 @@
|
|
3
3
|
|
4
4
|
#include <stdbool.h>
|
5
5
|
|
6
|
-
PgQueryFingerprintResult pg_query_fingerprint_with_opts(const char* input, bool printTokens);
|
6
|
+
extern PgQueryFingerprintResult pg_query_fingerprint_with_opts(const char* input, bool printTokens);
|
7
|
+
|
8
|
+
extern uint64_t pg_query_fingerprint_node(const void * node);
|
7
9
|
|
8
10
|
#endif
|
@@ -1,5 +1,6 @@
|
|
1
1
|
#include "pg_query.h"
|
2
2
|
#include "pg_query_internal.h"
|
3
|
+
#include "pg_query_fingerprint.h"
|
3
4
|
|
4
5
|
#include "parser/parser.h"
|
5
6
|
#include "parser/scanner.h"
|
@@ -14,6 +15,7 @@ typedef struct pgssLocationLen
|
|
14
15
|
{
|
15
16
|
int location; /* start offset in query text */
|
16
17
|
int length; /* length in bytes, or -1 to ignore */
|
18
|
+
int param_id; /* Param id to use - if negative prefix, need to abs(..) and add highest_extern_param_id */
|
17
19
|
} pgssLocationLen;
|
18
20
|
|
19
21
|
/*
|
@@ -30,14 +32,32 @@ typedef struct pgssConstLocations
|
|
30
32
|
/* Current number of valid entries in clocations array */
|
31
33
|
int clocations_count;
|
32
34
|
|
35
|
+
/* highest Param id we have assigned, not yet taking into account external param refs */
|
36
|
+
int highest_normalize_param_id;
|
37
|
+
|
33
38
|
/* highest Param id we've seen, in order to start normalization correctly */
|
34
39
|
int highest_extern_param_id;
|
35
40
|
|
36
41
|
/* query text */
|
37
42
|
const char * query;
|
38
43
|
int query_len;
|
44
|
+
|
45
|
+
/* optional recording of assigned or discovered param refs, only active if param_refs is not NULL */
|
46
|
+
int *param_refs;
|
47
|
+
int param_refs_buf_size;
|
48
|
+
int param_refs_count;
|
39
49
|
} pgssConstLocations;
|
40
50
|
|
51
|
+
/*
|
52
|
+
* Intermediate working state struct to remember param refs for individual target list elements
|
53
|
+
*/
|
54
|
+
typedef struct FpAndParamRefs
|
55
|
+
{
|
56
|
+
uint64_t fp;
|
57
|
+
int* param_refs;
|
58
|
+
int param_refs_count;
|
59
|
+
} FpAndParamRefs;
|
60
|
+
|
41
61
|
/*
|
42
62
|
* comp_location: comparator for qsorting pgssLocationLen structs by location
|
43
63
|
*/
|
@@ -230,7 +250,8 @@ generate_normalized_query(pgssConstLocations *jstate, int query_loc, int* query_
|
|
230
250
|
for (i = 0; i < jstate->clocations_count; i++)
|
231
251
|
{
|
232
252
|
int off, /* Offset from start for cur tok */
|
233
|
-
tok_len
|
253
|
+
tok_len, /* Length (in bytes) of that tok */
|
254
|
+
param_id; /* Param ID to be assigned */
|
234
255
|
|
235
256
|
off = jstate->clocations[i].location;
|
236
257
|
/* Adjust recorded location if we're dealing with partial string */
|
@@ -250,8 +271,10 @@ generate_normalized_query(pgssConstLocations *jstate, int query_loc, int* query_
|
|
250
271
|
n_quer_loc += len_to_wrt;
|
251
272
|
|
252
273
|
/* And insert a param symbol in place of the constant token */
|
253
|
-
|
254
|
-
|
274
|
+
param_id = (jstate->clocations[i].param_id < 0) ?
|
275
|
+
jstate->highest_extern_param_id + abs(jstate->clocations[i].param_id) :
|
276
|
+
jstate->clocations[i].param_id;
|
277
|
+
n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d", param_id);
|
255
278
|
|
256
279
|
quer_loc = off + tok_len;
|
257
280
|
last_off = off;
|
@@ -292,6 +315,18 @@ static void RecordConstLocation(pgssConstLocations *jstate, int location)
|
|
292
315
|
jstate->clocations[jstate->clocations_count].location = location;
|
293
316
|
/* initialize lengths to -1 to simplify fill_in_constant_lengths */
|
294
317
|
jstate->clocations[jstate->clocations_count].length = -1;
|
318
|
+
/* by default we assume that we need a new param ref */
|
319
|
+
jstate->clocations[jstate->clocations_count].param_id = - jstate->highest_normalize_param_id;
|
320
|
+
jstate->highest_normalize_param_id++;
|
321
|
+
/* record param ref number if requested */
|
322
|
+
if (jstate->param_refs != NULL) {
|
323
|
+
jstate->param_refs[jstate->param_refs_count] = jstate->clocations[jstate->clocations_count].param_id;
|
324
|
+
jstate->param_refs_count++;
|
325
|
+
if (jstate->param_refs_count >= jstate->param_refs_buf_size) {
|
326
|
+
jstate->param_refs_buf_size *= 2;
|
327
|
+
jstate->param_refs = (int *) repalloc(jstate->param_refs, jstate->param_refs_buf_size * sizeof(int));
|
328
|
+
}
|
329
|
+
}
|
295
330
|
jstate->clocations_count++;
|
296
331
|
}
|
297
332
|
}
|
@@ -313,6 +348,15 @@ static bool const_record_walker(Node *node, pgssConstLocations *jstate)
|
|
313
348
|
/* Track the highest ParamRef number */
|
314
349
|
if (((ParamRef *) node)->number > jstate->highest_extern_param_id)
|
315
350
|
jstate->highest_extern_param_id = castNode(ParamRef, node)->number;
|
351
|
+
|
352
|
+
if (jstate->param_refs != NULL) {
|
353
|
+
jstate->param_refs[jstate->param_refs_count] = ((ParamRef *) node)->number;
|
354
|
+
jstate->param_refs_count++;
|
355
|
+
if (jstate->param_refs_count >= jstate->param_refs_buf_size) {
|
356
|
+
jstate->param_refs_buf_size *= 2;
|
357
|
+
jstate->param_refs = (int *) repalloc(jstate->param_refs, jstate->param_refs_buf_size * sizeof(int));
|
358
|
+
}
|
359
|
+
}
|
316
360
|
}
|
317
361
|
break;
|
318
362
|
case T_DefElem:
|
@@ -343,39 +387,95 @@ static bool const_record_walker(Node *node, pgssConstLocations *jstate)
|
|
343
387
|
return const_record_walker((Node *) ((AlterRoleStmt *) node)->options, jstate);
|
344
388
|
case T_DeclareCursorStmt:
|
345
389
|
return const_record_walker((Node *) ((DeclareCursorStmt *) node)->query, jstate);
|
390
|
+
case T_TypeName:
|
391
|
+
/* Don't normalize constants in typmods or arrayBounds */
|
392
|
+
return false;
|
346
393
|
case T_SelectStmt:
|
347
394
|
{
|
348
395
|
SelectStmt *stmt = (SelectStmt *) node;
|
349
396
|
ListCell *lc;
|
397
|
+
List *fp_and_param_refs_list = NIL;
|
350
398
|
|
351
399
|
if (const_record_walker((Node *) stmt->distinctClause, jstate))
|
352
400
|
return true;
|
353
401
|
if (const_record_walker((Node *) stmt->intoClause, jstate))
|
354
402
|
return true;
|
355
|
-
|
356
|
-
|
403
|
+
foreach(lc, stmt->targetList)
|
404
|
+
{
|
405
|
+
ResTarget *res_target = lfirst_node(ResTarget, lc);
|
406
|
+
FpAndParamRefs *fp_and_param_refs = palloc0(sizeof(FpAndParamRefs));
|
407
|
+
|
408
|
+
/* Save all param refs we encounter or assign */
|
409
|
+
jstate->param_refs = palloc0(1 * sizeof(int));
|
410
|
+
jstate->param_refs_buf_size = 1;
|
411
|
+
jstate->param_refs_count = 0;
|
412
|
+
|
413
|
+
/* Walk the element */
|
414
|
+
if (const_record_walker((Node *) res_target, jstate))
|
415
|
+
return true;
|
416
|
+
|
417
|
+
/* Remember fingerprint and param refs for later */
|
418
|
+
fp_and_param_refs->fp = pg_query_fingerprint_node(res_target->val);
|
419
|
+
fp_and_param_refs->param_refs = jstate->param_refs;
|
420
|
+
fp_and_param_refs->param_refs_count = jstate->param_refs_count;
|
421
|
+
fp_and_param_refs_list = lappend(fp_and_param_refs_list, fp_and_param_refs);
|
422
|
+
|
423
|
+
/* Reset for next element, or stop recording if this is the last element */
|
424
|
+
jstate->param_refs = NULL;
|
425
|
+
jstate->param_refs_buf_size = 0;
|
426
|
+
jstate->param_refs_count = 0;
|
427
|
+
}
|
357
428
|
if (const_record_walker((Node *) stmt->fromClause, jstate))
|
358
429
|
return true;
|
359
430
|
if (const_record_walker((Node *) stmt->whereClause, jstate))
|
360
431
|
return true;
|
361
432
|
|
362
|
-
|
363
|
-
|
433
|
+
/*
|
434
|
+
* Instead of walking all of groupClause (like raw_expression_tree_walker does),
|
435
|
+
* only walk certain items.
|
436
|
+
*/
|
364
437
|
foreach(lc, stmt->groupClause)
|
365
438
|
{
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
439
|
+
/*
|
440
|
+
* Do not walk A_Const values that are simple integers, this avoids
|
441
|
+
* turning "GROUP BY 1" into "GROUP BY $n", which obscures an important
|
442
|
+
* semantic meaning. This matches how pg_stat_statements handles the
|
443
|
+
* GROUP BY clause (i.e. it doesn't touch these constants)
|
444
|
+
*/
|
370
445
|
if (IsA(lfirst(lc), A_Const) && IsA(&castNode(A_Const, lfirst(lc))->val, Integer))
|
371
446
|
continue;
|
372
447
|
|
448
|
+
/*
|
449
|
+
* Match up GROUP BY clauses against the target list, to assign the same
|
450
|
+
* param refs as used in the target list - this ensures the query is valid,
|
451
|
+
* instead of throwing a bogus "columns ... must appear in the GROUP BY
|
452
|
+
* clause or be used in an aggregate function" error
|
453
|
+
*/
|
454
|
+
uint64_t fp = pg_query_fingerprint_node(lfirst(lc));
|
455
|
+
FpAndParamRefs *fppr = NULL;
|
456
|
+
ListCell *lc2;
|
457
|
+
foreach(lc2, fp_and_param_refs_list) {
|
458
|
+
if (fp == ((FpAndParamRefs *) lfirst(lc2))->fp) {
|
459
|
+
fppr = (FpAndParamRefs *) lfirst(lc2);
|
460
|
+
foreach_delete_current(fp_and_param_refs_list, lc2);
|
461
|
+
break;
|
462
|
+
}
|
463
|
+
}
|
464
|
+
|
465
|
+
int prev_cloc_count = jstate->clocations_count;
|
373
466
|
if (const_record_walker((Node *) lfirst(lc), jstate))
|
374
467
|
return true;
|
468
|
+
|
469
|
+
if (fppr != NULL && fppr->param_refs_count == jstate->clocations_count - prev_cloc_count) {
|
470
|
+
for (int i = prev_cloc_count; i < jstate->clocations_count; i++) {
|
471
|
+
jstate->clocations[i].param_id = fppr->param_refs[i - prev_cloc_count];
|
472
|
+
}
|
473
|
+
jstate->highest_normalize_param_id -= fppr->param_refs_count;
|
474
|
+
}
|
375
475
|
}
|
376
476
|
foreach(lc, stmt->sortClause)
|
377
477
|
{
|
378
|
-
|
478
|
+
/* Similarly, don't turn "ORDER BY 1" into "ORDER BY $n" */
|
379
479
|
if (IsA(lfirst(lc), SortBy) && IsA(castNode(SortBy, lfirst(lc))->node, A_Const) &&
|
380
480
|
IsA(&castNode(A_Const, castNode(SortBy, lfirst(lc))->node)->val, Integer))
|
381
481
|
continue;
|
@@ -445,9 +545,13 @@ PgQueryNormalizeResult pg_query_normalize(const char* input)
|
|
445
545
|
jstate.clocations = (pgssLocationLen *)
|
446
546
|
palloc(jstate.clocations_buf_size * sizeof(pgssLocationLen));
|
447
547
|
jstate.clocations_count = 0;
|
548
|
+
jstate.highest_normalize_param_id = 1;
|
448
549
|
jstate.highest_extern_param_id = 0;
|
449
550
|
jstate.query = input;
|
450
551
|
jstate.query_len = query_len;
|
552
|
+
jstate.param_refs = NULL;
|
553
|
+
jstate.param_refs_buf_size = 0;
|
554
|
+
jstate.param_refs_count = 0;
|
451
555
|
|
452
556
|
/* Walk tree and record const locations */
|
453
557
|
const_record_walker((Node *) tree, &jstate);
|
@@ -466,6 +570,8 @@ PgQueryNormalizeResult pg_query_normalize(const char* input)
|
|
466
570
|
error = malloc(sizeof(PgQueryError));
|
467
571
|
error->message = strdup(error_data->message);
|
468
572
|
error->filename = strdup(error_data->filename);
|
573
|
+
error->funcname = strdup(error_data->funcname);
|
574
|
+
error->context = NULL;
|
469
575
|
error->lineno = error_data->lineno;
|
470
576
|
error->cursorpos = error_data->cursorpos;
|
471
577
|
|
@@ -484,6 +590,7 @@ void pg_query_free_normalize_result(PgQueryNormalizeResult result)
|
|
484
590
|
if (result.error) {
|
485
591
|
free(result.error->message);
|
486
592
|
free(result.error->filename);
|
593
|
+
free(result.error->funcname);
|
487
594
|
free(result.error);
|
488
595
|
}
|
489
596
|
|
@@ -108,7 +108,7 @@ static PLpgSQL_function *compile_create_function_stmt(CreateFunctionStmt* stmt)
|
|
108
108
|
}
|
109
109
|
}
|
110
110
|
|
111
|
-
assert(proc_source);
|
111
|
+
assert(proc_source != NULL);
|
112
112
|
|
113
113
|
if (stmt->returnType != NULL) {
|
114
114
|
foreach(lc3, stmt->returnType->names)
|
@@ -179,6 +179,26 @@ static PLpgSQL_function *compile_create_function_stmt(CreateFunctionStmt* stmt)
|
|
179
179
|
plpgsql_DumpExecTree = false;
|
180
180
|
plpgsql_start_datums();
|
181
181
|
|
182
|
+
/* Setup parameter names */
|
183
|
+
foreach(lc, stmt->parameters)
|
184
|
+
{
|
185
|
+
FunctionParameter *param = lfirst_node(FunctionParameter, lc);
|
186
|
+
if (param->name != NULL)
|
187
|
+
{
|
188
|
+
char buf[32];
|
189
|
+
PLpgSQL_type *argdtype;
|
190
|
+
PLpgSQL_variable *argvariable;
|
191
|
+
PLpgSQL_nsitem_type argitemtype;
|
192
|
+
snprintf(buf, sizeof(buf), "$%d", foreach_current_index(lc) + 1);
|
193
|
+
argdtype = plpgsql_build_datatype(UNKNOWNOID, -1, InvalidOid, NULL);
|
194
|
+
argvariable = plpgsql_build_variable(param->name ? param->name : buf, 0, argdtype, false);
|
195
|
+
argitemtype = argvariable->dtype == PLPGSQL_DTYPE_VAR ? PLPGSQL_NSTYPE_VAR : PLPGSQL_NSTYPE_REC;
|
196
|
+
plpgsql_ns_additem(argitemtype, argvariable->dno, buf);
|
197
|
+
if (param->name != NULL)
|
198
|
+
plpgsql_ns_additem(argitemtype, argvariable->dno, param->name);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
|
182
202
|
/* Set up as though in a function returning VOID */
|
183
203
|
function->fn_rettype = VOIDOID;
|
184
204
|
function->fn_retset = is_setof;
|
@@ -6147,7 +6147,7 @@ plpgsql_sql_error_callback(void *arg)
|
|
6147
6147
|
* This is handled the same as in check_sql_expr(), and we likewise
|
6148
6148
|
* expect that the given string is a copy from the source text.
|
6149
6149
|
*/
|
6150
|
-
static PLpgSQL_type * parse_datatype(const char *string, int location) { PLpgSQL_type *typ; typ = (PLpgSQL_type *) palloc0(sizeof(PLpgSQL_type)); typ->typname = pstrdup(string); typ->ttype = PLPGSQL_TTYPE_SCALAR; return typ; }
|
6150
|
+
static PLpgSQL_type * parse_datatype(const char *string, int location) { PLpgSQL_type *typ; typ = (PLpgSQL_type *) palloc0(sizeof(PLpgSQL_type)); typ->typname = pstrdup(string); typ->ttype = strcmp(string, "RECORD") == 0 ? PLPGSQL_TTYPE_REC : PLPGSQL_TTYPE_SCALAR; return typ; }
|
6151
6151
|
|
6152
6152
|
|
6153
6153
|
/*
|
data/lib/pg_query/parse.rb
CHANGED
@@ -89,6 +89,10 @@ module PgQuery
|
|
89
89
|
|
90
90
|
protected
|
91
91
|
|
92
|
+
# Parses the query and finds table and function references
|
93
|
+
#
|
94
|
+
# Note we use ".to_ary" on arrays from the Protobuf library before
|
95
|
+
# passing them to concat, because of https://bugs.ruby-lang.org/issues/18140
|
92
96
|
def load_objects! # rubocop:disable Metrics/CyclomaticComplexity
|
93
97
|
@tables = [] # types: select, dml, ddl
|
94
98
|
@cte_names = []
|
@@ -108,20 +112,16 @@ module PgQuery
|
|
108
112
|
# The following statement types do not modify tables and are added to from_clause_items
|
109
113
|
# (and subsequently @tables)
|
110
114
|
when :select_stmt
|
111
|
-
subselect_items.concat(statement.select_stmt.target_list)
|
115
|
+
subselect_items.concat(statement.select_stmt.target_list.to_ary)
|
112
116
|
subselect_items << statement.select_stmt.where_clause if statement.select_stmt.where_clause
|
113
117
|
subselect_items.concat(statement.select_stmt.sort_clause.collect { |h| h.sort_by.node })
|
114
|
-
subselect_items.concat(statement.select_stmt.group_clause)
|
118
|
+
subselect_items.concat(statement.select_stmt.group_clause.to_ary)
|
115
119
|
subselect_items << statement.select_stmt.having_clause if statement.select_stmt.having_clause
|
116
120
|
|
117
121
|
case statement.select_stmt.op
|
118
122
|
when :SETOP_NONE
|
119
123
|
(statement.select_stmt.from_clause || []).each do |item|
|
120
|
-
|
121
|
-
statements << item.range_subselect.subquery
|
122
|
-
else
|
123
|
-
from_clause_items << { item: item, type: :select }
|
124
|
-
end
|
124
|
+
from_clause_items << { item: item, type: :select }
|
125
125
|
end
|
126
126
|
when :SETOP_UNION
|
127
127
|
statements << PgQuery::Node.new(select_stmt: statement.select_stmt.larg) if statement.select_stmt.larg
|
@@ -143,7 +143,7 @@ module PgQuery
|
|
143
143
|
value.from_clause.each do |item|
|
144
144
|
from_clause_items << { item: item, type: :select }
|
145
145
|
end
|
146
|
-
subselect_items.concat(statement.update_stmt.target_list)
|
146
|
+
subselect_items.concat(statement.update_stmt.target_list.to_ary)
|
147
147
|
end
|
148
148
|
|
149
149
|
subselect_items << statement.update_stmt.where_clause if statement.node == :update_stmt && statement.update_stmt.where_clause
|
@@ -236,6 +236,8 @@ module PgQuery
|
|
236
236
|
next_item = subselect_items.shift
|
237
237
|
if next_item
|
238
238
|
case next_item.node
|
239
|
+
when :list
|
240
|
+
subselect_items += next_item.list.items
|
239
241
|
when :a_expr
|
240
242
|
%w[lexpr rexpr].each do |side|
|
241
243
|
elem = next_item.a_expr.public_send(side)
|
@@ -247,56 +249,54 @@ module PgQuery
|
|
247
249
|
end
|
248
250
|
end
|
249
251
|
when :bool_expr
|
250
|
-
subselect_items.concat(next_item.bool_expr.args)
|
252
|
+
subselect_items.concat(next_item.bool_expr.args.to_ary)
|
251
253
|
when :coalesce_expr
|
252
|
-
subselect_items.concat(next_item.coalesce_expr.args)
|
254
|
+
subselect_items.concat(next_item.coalesce_expr.args.to_ary)
|
253
255
|
when :min_max_expr
|
254
|
-
subselect_items.concat(next_item.min_max_expr.args)
|
256
|
+
subselect_items.concat(next_item.min_max_expr.args.to_ary)
|
255
257
|
when :res_target
|
256
258
|
subselect_items << next_item.res_target.val
|
257
259
|
when :sub_link
|
258
260
|
statements << next_item.sub_link.subselect
|
259
261
|
when :func_call
|
262
|
+
subselect_items.concat(next_item.func_call.args.to_ary)
|
260
263
|
@functions << {
|
261
|
-
function: next_item.func_call.funcname
|
264
|
+
function: next_item.func_call.funcname.map { |f| f.string.str }.join('.'),
|
262
265
|
type: :call
|
263
266
|
}
|
264
267
|
end
|
265
268
|
end
|
266
269
|
|
267
|
-
break if subselect_items.empty? && statements.empty?
|
268
|
-
end
|
269
|
-
|
270
|
-
loop do
|
271
270
|
next_item = from_clause_items.shift
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
from_clause_items += from_clause.map { |r| { item: r, type: next_item[:type] } } if from_clause
|
271
|
+
if next_item && next_item[:item]
|
272
|
+
case next_item[:item].node
|
273
|
+
when :join_expr
|
274
|
+
from_clause_items << { item: next_item[:item].join_expr.larg, type: next_item[:type] }
|
275
|
+
from_clause_items << { item: next_item[:item].join_expr.rarg, type: next_item[:type] }
|
276
|
+
when :row_expr
|
277
|
+
from_clause_items += next_item[:item].row_expr.args.map { |a| { item: a, type: next_item[:type] } }
|
278
|
+
when :range_var
|
279
|
+
rangevar = next_item[:item].range_var
|
280
|
+
next if rangevar.schemaname.empty? && @cte_names.include?(rangevar.relname)
|
281
|
+
|
282
|
+
table = [rangevar.schemaname, rangevar.relname].reject { |s| s.nil? || s.empty? }.join('.')
|
283
|
+
@tables << {
|
284
|
+
name: table,
|
285
|
+
type: next_item[:type],
|
286
|
+
location: rangevar.location,
|
287
|
+
schemaname: (rangevar.schemaname unless rangevar.schemaname.empty?),
|
288
|
+
relname: rangevar.relname,
|
289
|
+
inh: rangevar.inh
|
290
|
+
}
|
291
|
+
@aliases[rangevar.alias.aliasname] = table if rangevar.alias
|
292
|
+
when :range_subselect
|
293
|
+
statements << next_item[:item].range_subselect.subquery
|
294
|
+
when :range_function
|
295
|
+
subselect_items += next_item[:item].range_function.functions
|
296
|
+
end
|
299
297
|
end
|
298
|
+
|
299
|
+
break if subselect_items.empty? && statements.empty? && from_clause_items.empty?
|
300
300
|
end
|
301
301
|
|
302
302
|
@tables.uniq!
|
data/lib/pg_query/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_query
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lukas Fittl
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -561,7 +561,7 @@ files:
|
|
561
561
|
- lib/pg_query/treewalker.rb
|
562
562
|
- lib/pg_query/truncate.rb
|
563
563
|
- lib/pg_query/version.rb
|
564
|
-
homepage:
|
564
|
+
homepage: https://github.com/pganalyze/pg_query
|
565
565
|
licenses:
|
566
566
|
- BSD-3-Clause
|
567
567
|
metadata: {}
|