pg_query 6.2.2 → 6.2.5
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 +54 -1
- data/README.md +25 -2
- data/Rakefile +2 -2
- data/ext/pg_query/include/pg_query_fingerprint_defs.c +8 -0
- data/ext/pg_query/include/pg_query_outfuncs_defs.c +4 -0
- data/ext/pg_query/include/pg_query_readfuncs_defs.c +4 -0
- data/ext/pg_query/include/postgres/copyfuncs.funcs.c +4 -0
- data/ext/pg_query/include/postgres/equalfuncs.funcs.c +4 -0
- data/ext/pg_query/include/postgres/nodes/parsenodes.h +8 -0
- data/ext/pg_query/include/protobuf/pg_query.pb-c.h +282 -278
- data/ext/pg_query/pg_query.pb-c.c +64 -12
- data/ext/pg_query/pg_query_normalize.c +45 -47
- data/ext/pg_query/pg_query_readfuncs_protobuf.c +8 -5
- data/ext/pg_query/pg_query_ruby.c +41 -0
- data/ext/pg_query/postgres_deparse.c +234 -13
- data/ext/pg_query/src_backend_nodes_makefuncs.c +2 -0
- data/ext/pg_query/src_backend_parser_gram.c +30322 -30090
- data/lib/pg_query/fingerprint.rb +6 -0
- data/lib/pg_query/parse.rb +95 -1
- data/lib/pg_query/pg_query_pb.rb +1 -1
- data/lib/pg_query/summary.rb +145 -0
- data/lib/pg_query/version.rb +1 -1
- data/lib/pg_query.rb +1 -0
- metadata +6 -5
data/lib/pg_query/fingerprint.rb
CHANGED
|
@@ -93,6 +93,12 @@ module PgQuery
|
|
|
93
93
|
case postgres_field_name
|
|
94
94
|
when 'location'
|
|
95
95
|
next
|
|
96
|
+
when 'arg_location'
|
|
97
|
+
next if node.is_a?(DefElem)
|
|
98
|
+
when 'payload_location'
|
|
99
|
+
next if node.is_a?(NotifyStmt)
|
|
100
|
+
when 'conninfo_location'
|
|
101
|
+
next if [CreateSubscriptionStmt, AlterSubscriptionStmt].include?(node.class)
|
|
96
102
|
when 'name'
|
|
97
103
|
next if [PrepareStmt, ExecuteStmt, DeallocateStmt, FunctionParameter].include?(node.class)
|
|
98
104
|
next if node.is_a?(ResTarget) && parent_node_name == 'SelectStmt' && parent_field_name == 'targetList'
|
data/lib/pg_query/parse.rb
CHANGED
|
@@ -102,6 +102,7 @@ module PgQuery
|
|
|
102
102
|
@cte_names = []
|
|
103
103
|
@aliases = {}
|
|
104
104
|
@functions = [] # types: call, ddl
|
|
105
|
+
@cte_self_reference_locations = []
|
|
105
106
|
|
|
106
107
|
statements = @tree.stmts.dup.to_a.map(&:stmt)
|
|
107
108
|
from_clause_items = [] # types: select, dml, ddl
|
|
@@ -122,6 +123,7 @@ module PgQuery
|
|
|
122
123
|
subselect_items.concat(statement.select_stmt.sort_clause.collect { |h| h.sort_by.node })
|
|
123
124
|
subselect_items.concat(statement.select_stmt.group_clause.to_ary)
|
|
124
125
|
subselect_items << statement.select_stmt.having_clause if statement.select_stmt.having_clause
|
|
126
|
+
subselect_items.concat(statement.select_stmt.values_lists.to_ary)
|
|
125
127
|
|
|
126
128
|
case statement.select_stmt.op
|
|
127
129
|
when :SETOP_NONE
|
|
@@ -134,6 +136,7 @@ module PgQuery
|
|
|
134
136
|
end
|
|
135
137
|
|
|
136
138
|
if statement.select_stmt.with_clause
|
|
139
|
+
record_cte_self_references!(statement.select_stmt.with_clause)
|
|
137
140
|
cte_statements, cte_names = statements_and_cte_names_for_with_clause(statement.select_stmt.with_clause)
|
|
138
141
|
@cte_names.concat(cte_names)
|
|
139
142
|
statements.concat(cte_statements)
|
|
@@ -164,7 +167,15 @@ module PgQuery
|
|
|
164
167
|
end
|
|
165
168
|
end
|
|
166
169
|
|
|
170
|
+
subselect_items.concat(value.returning_list.to_ary)
|
|
171
|
+
|
|
172
|
+
if statement.node == :insert_stmt && value.on_conflict_clause
|
|
173
|
+
subselect_items.concat(value.on_conflict_clause.target_list.to_ary)
|
|
174
|
+
subselect_items << value.on_conflict_clause.where_clause if value.on_conflict_clause.where_clause
|
|
175
|
+
end
|
|
176
|
+
|
|
167
177
|
if value.with_clause
|
|
178
|
+
record_cte_self_references!(value.with_clause)
|
|
168
179
|
cte_statements, cte_names = statements_and_cte_names_for_with_clause(value.with_clause)
|
|
169
180
|
@cte_names.concat(cte_names)
|
|
170
181
|
statements.concat(cte_statements)
|
|
@@ -294,10 +305,20 @@ module PgQuery
|
|
|
294
305
|
when :func_call
|
|
295
306
|
# See also CALL below
|
|
296
307
|
subselect_items.concat(next_item.func_call.args.to_ary)
|
|
308
|
+
subselect_items.concat(next_item.func_call.agg_order.to_ary)
|
|
309
|
+
subselect_items << next_item.func_call.agg_filter if next_item.func_call.agg_filter
|
|
310
|
+
subselect_items << PgQuery::Node.new(window_def: next_item.func_call.over) if next_item.func_call.over
|
|
297
311
|
@functions << {
|
|
298
312
|
function: next_item.func_call.funcname.map { |f| f.string.sval }.join('.'),
|
|
299
313
|
type: :call
|
|
300
314
|
}
|
|
315
|
+
when :sort_by
|
|
316
|
+
subselect_items << next_item.sort_by.node
|
|
317
|
+
when :window_def
|
|
318
|
+
subselect_items.concat(next_item.window_def.partition_clause.to_ary)
|
|
319
|
+
subselect_items.concat(next_item.window_def.order_clause.to_ary)
|
|
320
|
+
subselect_items << next_item.window_def.start_offset if next_item.window_def.start_offset
|
|
321
|
+
subselect_items << next_item.window_def.end_offset if next_item.window_def.end_offset
|
|
301
322
|
when :case_expr
|
|
302
323
|
subselect_items.concat(next_item.case_expr.args.map { |arg| arg.case_when.expr })
|
|
303
324
|
subselect_items.concat(next_item.case_expr.args.map { |arg| arg.case_when.result })
|
|
@@ -329,7 +350,7 @@ module PgQuery
|
|
|
329
350
|
from_clause_items += next_item[:item].row_expr.args.map { |a| { item: a, type: next_item[:type] } }
|
|
330
351
|
when :range_var
|
|
331
352
|
rangevar = next_item[:item].range_var
|
|
332
|
-
next if
|
|
353
|
+
next if cte_reference?(rangevar, next_item[:type])
|
|
333
354
|
|
|
334
355
|
table = [rangevar.schemaname, rangevar.relname].reject { |s| s.nil? || s.empty? }.join('.')
|
|
335
356
|
@tables << {
|
|
@@ -368,5 +389,78 @@ module PgQuery
|
|
|
368
389
|
|
|
369
390
|
[statements, cte_names]
|
|
370
391
|
end
|
|
392
|
+
|
|
393
|
+
# Determines whether a RangeVar refers to a CTE rather than to a relation.
|
|
394
|
+
#
|
|
395
|
+
# Only plain (SELECT-style) references can resolve to a CTE: a CTE is not a
|
|
396
|
+
# valid target for DML (INSERT/UPDATE/DELETE/COPY) or DDL, so those always
|
|
397
|
+
# name a real relation even when a CTE in the same statement shares the name.
|
|
398
|
+
def cte_reference?(rangevar, type)
|
|
399
|
+
return false unless type == :select
|
|
400
|
+
return false unless rangevar.schemaname.empty?
|
|
401
|
+
return false unless @cte_names.include?(rangevar.relname)
|
|
402
|
+
|
|
403
|
+
!@cte_self_reference_locations.include?(rangevar.location)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# A non-recursive CTE is not visible inside its own definition, so a
|
|
407
|
+
# reference to its own name there resolves to a real relation:
|
|
408
|
+
#
|
|
409
|
+
# WITH users AS (SELECT * FROM users) SELECT * FROM users
|
|
410
|
+
# ^^^^^ the table ^^^^^ the CTE
|
|
411
|
+
#
|
|
412
|
+
# Records the locations of those self-references so they are not mistaken
|
|
413
|
+
# for CTE references. Locations uniquely identify a RangeVar occurrence,
|
|
414
|
+
# which keeps the outer (genuine) CTE reference above excluded.
|
|
415
|
+
def record_cte_self_references!(with_clause)
|
|
416
|
+
return if with_clause.recursive
|
|
417
|
+
|
|
418
|
+
with_clause.ctes.each do |item|
|
|
419
|
+
next unless item.node == :common_table_expr
|
|
420
|
+
|
|
421
|
+
record_self_references_for_cte!(item.common_table_expr)
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def record_self_references_for_cte!(cte)
|
|
426
|
+
return if cte.ctequery.nil?
|
|
427
|
+
return if @cte_names.include?(cte.ctename)
|
|
428
|
+
|
|
429
|
+
record_self_references_in(cte.ctequery, cte.ctename)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# Depth-first scan of a CTE definition for unqualified RangeVars matching
|
|
433
|
+
# the CTE's own name.
|
|
434
|
+
#
|
|
435
|
+
# This deliberately avoids #walk!, which yields every node and iterates all
|
|
436
|
+
# of PgQuery::Node's oneof fields. Following the oneof directly via #inner
|
|
437
|
+
# keeps this proportional to the nodes actually present.
|
|
438
|
+
def record_self_references_in(node, cte_name)
|
|
439
|
+
case node
|
|
440
|
+
when PgQuery::Node
|
|
441
|
+
record_self_references_in(node.inner, cte_name)
|
|
442
|
+
when PgQuery::RangeVar
|
|
443
|
+
@cte_self_reference_locations << node.location if cte_self_reference?(node, cte_name)
|
|
444
|
+
when Google::Protobuf::RepeatedField
|
|
445
|
+
node.each { |child| record_self_references_in(child, cte_name) }
|
|
446
|
+
when Google::Protobuf::MessageExts
|
|
447
|
+
record_self_references_in_message(node, cte_name)
|
|
448
|
+
end
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def record_self_references_in_message(message, cte_name)
|
|
452
|
+
message.class.descriptor.each do |field_descriptor|
|
|
453
|
+
child = field_descriptor.get(message)
|
|
454
|
+
next unless child.is_a?(Google::Protobuf::MessageExts) || child.is_a?(Google::Protobuf::RepeatedField)
|
|
455
|
+
|
|
456
|
+
record_self_references_in(child, cte_name)
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
def cte_self_reference?(node, cte_name)
|
|
461
|
+
node.schemaname.empty? &&
|
|
462
|
+
node.relname == cte_name &&
|
|
463
|
+
!node.location.negative?
|
|
464
|
+
end
|
|
371
465
|
end
|
|
372
466
|
end
|