pg_query 2.1.0 → 2.1.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 +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: {}
|