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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ede23ff0f5d9af4599a7732fe624b3e2fba355ebb54bb6b88df3ef3932d7692a
4
- data.tar.gz: d11105ea7323b8908dde6b328908190858d150f12a13a109d943ae3bf8af475b
3
+ metadata.gz: b4db477ee95910b6c5d8e6c4e5f86c64cc985c0812cc461c017f652754848be1
4
+ data.tar.gz: 1167f0524fe8a23b19ee1beef4400cf5f0f817ba64f7d1893ac3d8ddb36ad0ce
5
5
  SHA512:
6
- metadata.gz: 37b3301df52b4743098ded9bbb69b05f4a360f7e1335386833ac619f4e5423f7b3c42a6506b3519c59ed3d0bb62e6e59ef04c8515fc9a3eb5daa95c53ab228e7
7
- data.tar.gz: cd94026574593d6e78ac41e88c3378a6c46ea6d0869f8ddd9a200c367a969ff67695a780b0609518eafcff7a8dfaff015ce6408662eff7616cb866b46b52593a
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.6'.freeze
9
- LIB_PG_QUERY_SHA256SUM = '61f384ac949bd7404efe6bcc37a8a6fca79030e59c02659f108ee1db45e93414'.freeze
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'
@@ -987,3 +987,6 @@
987
987
  #undef HAVE_EXECINFO_H
988
988
  #undef HAVE_BACKTRACE_SYMBOLS
989
989
  #undef HAVE__GET_CPUID
990
+ #ifdef __FreeBSD__
991
+ #define HAVE_STRCHRNUL
992
+ #endif
@@ -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; /* Length (in bytes) of that tok */
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
- n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d",
254
- i + 1 + jstate->highest_extern_param_id);
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
- if (const_record_walker((Node *) stmt->targetList, jstate))
356
- return true;
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
- // Instead of walking all of groupClause (like raw_expression_tree_walker does),
363
- // only walk certain items.
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
- // Do not walk A_Const values that are simple integers, this avoids
367
- // turning "GROUP BY 1" into "GROUP BY $n", which obscures an important
368
- // semantic meaning. This matches how pg_stat_statements handles the
369
- // GROUP BY clause (i.e. it doesn't touch these constants)
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
- // Similarly, don't turn "ORDER BY 1" into "ORDER BY $n"
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;
@@ -1 +1,2 @@
1
1
  _Init_pg_query
2
+ Init_pg_query
@@ -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
  /*
@@ -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
- if item.node == :range_subselect
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[0].string.str,
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
- break unless next_item && next_item[:item]
273
-
274
- case next_item[:item].node
275
- when :join_expr
276
- from_clause_items << { item: next_item[:item].join_expr.larg, type: next_item[:type] }
277
- from_clause_items << { item: next_item[:item].join_expr.rarg, type: next_item[:type] }
278
- when :row_expr
279
- from_clause_items += next_item[:item].row_expr.args.map { |a| { item: a, type: next_item[:type] } }
280
- when :range_var
281
- rangevar = next_item[:item].range_var
282
- next if rangevar.schemaname.empty? && @cte_names.include?(rangevar.relname)
283
-
284
- table = [rangevar.schemaname, rangevar.relname].reject { |s| s.nil? || s.empty? }.join('.')
285
- @tables << {
286
- name: table,
287
- type: next_item[:type],
288
- location: rangevar.location,
289
- schemaname: (rangevar.schemaname unless rangevar.schemaname.empty?),
290
- relname: rangevar.relname,
291
- inh: rangevar.inh
292
- }
293
- @aliases[rangevar.alias.aliasname] = table if rangevar.alias
294
- when :range_subselect
295
- from_clause_items << { item: next_item[:item].range_subselect.subquery, type: next_item[:type] }
296
- when :select_stmt
297
- from_clause = next_item[:item].select_stmt.from_clause
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!
@@ -1,3 +1,3 @@
1
1
  module PgQuery
2
- VERSION = '2.1.0'.freeze
2
+ VERSION = '2.1.1'.freeze
3
3
  end
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.0
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-07-04 00:00:00.000000000 Z
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: http://github.com/pganalyze/pg_query
564
+ homepage: https://github.com/pganalyze/pg_query
565
565
  licenses:
566
566
  - BSD-3-Clause
567
567
  metadata: {}