pg_query 2.0.3 → 2.1.3

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +77 -0
  3. data/README.md +12 -0
  4. data/Rakefile +5 -19
  5. data/ext/pg_query/extconf.rb +3 -1
  6. data/ext/pg_query/include/c.h +12 -0
  7. data/ext/pg_query/include/executor/executor.h +6 -0
  8. data/ext/pg_query/include/nodes/execnodes.h +9 -6
  9. data/ext/pg_query/include/nodes/pathnodes.h +1 -1
  10. data/ext/pg_query/include/optimizer/paths.h +8 -0
  11. data/ext/pg_query/include/pg_config.h +9 -6
  12. data/ext/pg_query/include/pg_config_manual.h +7 -0
  13. data/ext/pg_query/include/pg_query.h +2 -2
  14. data/ext/pg_query/include/pg_query_outfuncs_defs.c +1 -0
  15. data/ext/pg_query/include/pg_query_readfuncs_defs.c +1 -0
  16. data/ext/pg_query/include/protobuf/pg_query.pb-c.h +472 -467
  17. data/ext/pg_query/include/protobuf-c/protobuf-c.h +7 -3
  18. data/ext/pg_query/include/protobuf-c.h +7 -3
  19. data/ext/pg_query/include/utils/array.h +1 -0
  20. data/ext/pg_query/include/utils/lsyscache.h +1 -0
  21. data/ext/pg_query/include/utils/probes.h +57 -57
  22. data/ext/pg_query/pg_query.pb-c.c +502 -487
  23. data/ext/pg_query/pg_query_deparse.c +6 -0
  24. data/ext/pg_query/pg_query_fingerprint.c +119 -32
  25. data/ext/pg_query/pg_query_fingerprint.h +3 -1
  26. data/ext/pg_query/pg_query_normalize.c +222 -63
  27. data/ext/pg_query/pg_query_parse_plpgsql.c +21 -1
  28. data/ext/pg_query/pg_query_ruby.c +1 -1
  29. data/ext/pg_query/pg_query_ruby.sym +1 -0
  30. data/ext/pg_query/protobuf-c.c +34 -27
  31. data/ext/pg_query/src_backend_utils_mmgr_mcxt.c +36 -0
  32. data/ext/pg_query/src_common_hashfn.c +420 -0
  33. data/ext/pg_query/src_pl_plpgsql_src_pl_gram.c +1 -1
  34. data/lib/pg_query/filter_columns.rb +4 -4
  35. data/lib/pg_query/fingerprint.rb +1 -3
  36. data/lib/pg_query/parse.rb +111 -45
  37. data/lib/pg_query/pg_query_pb.rb +1385 -1383
  38. data/lib/pg_query/version.rb +1 -1
  39. data/lib/pg_query.rb +0 -1
  40. metadata +8 -8
  41. data/lib/pg_query/json_field_names.rb +0 -1402
@@ -29,6 +29,7 @@ module PgQuery
29
29
  @tables = nil
30
30
  @aliases = nil
31
31
  @cte_names = nil
32
+ @functions = nil
32
33
  end
33
34
 
34
35
  def dup_tree
@@ -51,27 +52,52 @@ module PgQuery
51
52
  tables_with_details.select { |t| t[:type] == :ddl }.map { |t| t[:name] }.uniq
52
53
  end
53
54
 
55
+ # Returns function names, ignoring their argument types. This may be insufficient
56
+ # if you need to disambiguate two functions with the same name but different argument
57
+ # types.
58
+ def functions
59
+ functions_with_details.map { |f| f[:function] }.uniq
60
+ end
61
+
62
+ def ddl_functions
63
+ functions_with_details.select { |f| f[:type] == :ddl }.map { |f| f[:function] }.uniq
64
+ end
65
+
66
+ def call_functions
67
+ functions_with_details.select { |f| f[:type] == :call }.map { |f| f[:function] }.uniq
68
+ end
69
+
54
70
  def cte_names
55
- load_tables_and_aliases! if @cte_names.nil?
71
+ load_objects! if @cte_names.nil?
56
72
  @cte_names
57
73
  end
58
74
 
59
75
  def aliases
60
- load_tables_and_aliases! if @aliases.nil?
76
+ load_objects! if @aliases.nil?
61
77
  @aliases
62
78
  end
63
79
 
64
80
  def tables_with_details
65
- load_tables_and_aliases! if @tables.nil?
81
+ load_objects! if @tables.nil?
66
82
  @tables
67
83
  end
68
84
 
85
+ def functions_with_details
86
+ load_objects! if @functions.nil?
87
+ @functions
88
+ end
89
+
69
90
  protected
70
91
 
71
- def load_tables_and_aliases! # rubocop:disable Metrics/CyclomaticComplexity
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
96
+ def load_objects! # rubocop:disable Metrics/CyclomaticComplexity
72
97
  @tables = [] # types: select, dml, ddl
73
98
  @cte_names = []
74
99
  @aliases = {}
100
+ @functions = [] # types: call, ddl
75
101
 
76
102
  statements = @tree.stmts.dup.to_a.map(&:stmt)
77
103
  from_clause_items = [] # types: select, dml, ddl
@@ -86,22 +112,18 @@ module PgQuery
86
112
  # The following statement types do not modify tables and are added to from_clause_items
87
113
  # (and subsequently @tables)
88
114
  when :select_stmt
89
- subselect_items.concat(statement.select_stmt.target_list)
115
+ subselect_items.concat(statement.select_stmt.target_list.to_ary)
90
116
  subselect_items << statement.select_stmt.where_clause if statement.select_stmt.where_clause
91
117
  subselect_items.concat(statement.select_stmt.sort_clause.collect { |h| h.sort_by.node })
92
- subselect_items.concat(statement.select_stmt.group_clause)
118
+ subselect_items.concat(statement.select_stmt.group_clause.to_ary)
93
119
  subselect_items << statement.select_stmt.having_clause if statement.select_stmt.having_clause
94
120
 
95
121
  case statement.select_stmt.op
96
122
  when :SETOP_NONE
97
123
  (statement.select_stmt.from_clause || []).each do |item|
98
- if item.node == :range_subselect
99
- statements << item.range_subselect.subquery
100
- else
101
- from_clause_items << { item: item, type: :select }
102
- end
124
+ from_clause_items << { item: item, type: :select }
103
125
  end
104
- when :SETOP_UNION
126
+ when :SETOP_UNION, :SETOP_EXCEPT, :SETOP_INTERSECT
105
127
  statements << PgQuery::Node.new(select_stmt: statement.select_stmt.larg) if statement.select_stmt.larg
106
128
  statements << PgQuery::Node.new(select_stmt: statement.select_stmt.rarg) if statement.select_stmt.rarg
107
129
  end
@@ -117,10 +139,22 @@ module PgQuery
117
139
  from_clause_items << { item: PgQuery::Node.new(range_var: value.relation), type: :dml }
118
140
  statements << value.select_stmt if statement.node == :insert_stmt && value.select_stmt
119
141
 
120
- subselect_items.concat(statement.update_stmt.target_list) if statement.node == :update_stmt
142
+ if statement.node == :update_stmt
143
+ value.from_clause.each do |item|
144
+ from_clause_items << { item: item, type: :select }
145
+ end
146
+ subselect_items.concat(statement.update_stmt.target_list.to_ary)
147
+ end
148
+
121
149
  subselect_items << statement.update_stmt.where_clause if statement.node == :update_stmt && statement.update_stmt.where_clause
122
150
  subselect_items << statement.delete_stmt.where_clause if statement.node == :delete_stmt && statement.delete_stmt.where_clause
123
151
 
152
+ if statement.node == :delete_stmt
153
+ statement.delete_stmt.using_clause.each do |using_clause|
154
+ from_clause_items << { item: using_clause, type: :select }
155
+ end
156
+ end
157
+
124
158
  if value.with_clause
125
159
  cte_statements, cte_names = statements_and_cte_names_for_with_clause(value.with_clause)
126
160
  @cte_names.concat(cte_names)
@@ -168,6 +202,10 @@ module PgQuery
168
202
  @tables += objects.map { |r| { name: r.join('.'), type: :ddl } }
169
203
  when :OBJECT_RULE, :OBJECT_TRIGGER
170
204
  @tables += objects.map { |r| { name: r[0..-2].join('.'), type: :ddl } }
205
+ when :OBJECT_FUNCTION
206
+ # Only one function can be dropped in a statement
207
+ obj = statement.drop_stmt.objects[0].object_with_args
208
+ @functions << { function: obj.objname[0].string.str, type: :ddl }
171
209
  end
172
210
  when :grant_stmt
173
211
  objects = statement.grant_stmt.objects
@@ -184,12 +222,28 @@ module PgQuery
184
222
  # The following are other statements that don't fit into query/DML/DDL
185
223
  when :explain_stmt
186
224
  statements << statement.explain_stmt.query
225
+ when :create_function_stmt
226
+ @functions << {
227
+ function: statement.create_function_stmt.funcname[0].string.str,
228
+ type: :ddl
229
+ }
230
+ when :rename_stmt
231
+ if statement.rename_stmt.rename_type == :OBJECT_FUNCTION
232
+ original_name = statement.rename_stmt.object.object_with_args.objname[0].string.str
233
+ new_name = statement.rename_stmt.newname
234
+ @functions += [
235
+ { function: original_name, type: :ddl },
236
+ { function: new_name, type: :ddl }
237
+ ]
238
+ end
187
239
  end
188
240
  end
189
241
 
190
242
  next_item = subselect_items.shift
191
243
  if next_item
192
244
  case next_item.node
245
+ when :list
246
+ subselect_items += next_item.list.items.to_ary
193
247
  when :a_expr
194
248
  %w[lexpr rexpr].each do |side|
195
249
  elem = next_item.a_expr.public_send(side)
@@ -201,47 +255,59 @@ module PgQuery
201
255
  end
202
256
  end
203
257
  when :bool_expr
204
- subselect_items.concat(next_item.bool_expr.args)
258
+ subselect_items.concat(next_item.bool_expr.args.to_ary)
259
+ when :coalesce_expr
260
+ subselect_items.concat(next_item.coalesce_expr.args.to_ary)
261
+ when :min_max_expr
262
+ subselect_items.concat(next_item.min_max_expr.args.to_ary)
205
263
  when :res_target
206
264
  subselect_items << next_item.res_target.val
207
265
  when :sub_link
208
266
  statements << next_item.sub_link.subselect
267
+ when :func_call
268
+ subselect_items.concat(next_item.func_call.args.to_ary)
269
+ @functions << {
270
+ function: next_item.func_call.funcname.map { |f| f.string.str }.join('.'),
271
+ type: :call
272
+ }
273
+ when :case_expr
274
+ subselect_items.concat(next_item.case_expr.args.map { |arg| arg.case_when.expr })
275
+ subselect_items.concat(next_item.case_expr.args.map { |arg| arg.case_when.result })
276
+ subselect_items << next_item.case_expr.defresult
209
277
  end
210
278
  end
211
279
 
212
- break if subselect_items.empty? && statements.empty?
213
- end
214
-
215
- loop do
216
280
  next_item = from_clause_items.shift
217
- break unless next_item && next_item[:item]
218
-
219
- case next_item[:item].node
220
- when :join_expr
221
- from_clause_items << { item: next_item[:item].join_expr.larg, type: next_item[:type] }
222
- from_clause_items << { item: next_item[:item].join_expr.rarg, type: next_item[:type] }
223
- when :row_expr
224
- from_clause_items += next_item[:item].row_expr.args.map { |a| { item: a, type: next_item[:type] } }
225
- when :range_var
226
- rangevar = next_item[:item].range_var
227
- next if rangevar.schemaname.empty? && @cte_names.include?(rangevar.relname)
228
-
229
- table = [rangevar.schemaname, rangevar.relname].reject { |s| s.nil? || s.empty? }.join('.')
230
- @tables << {
231
- name: table,
232
- type: next_item[:type],
233
- location: rangevar.location,
234
- schemaname: (rangevar.schemaname unless rangevar.schemaname.empty?),
235
- relname: rangevar.relname,
236
- inh: rangevar.inh
237
- }
238
- @aliases[rangevar.alias.aliasname] = table if rangevar.alias
239
- when :range_subselect
240
- from_clause_items << { item: next_item[:item].range_subselect.subquery, type: next_item[:type] }
241
- when :select_stmt
242
- from_clause = next_item[:item].select_stmt.from_clause
243
- from_clause_items += from_clause.map { |r| { item: r, type: next_item[:type] } } if from_clause
281
+ if next_item && next_item[:item]
282
+ case next_item[:item].node
283
+ when :join_expr
284
+ from_clause_items << { item: next_item[:item].join_expr.larg, type: next_item[:type] }
285
+ from_clause_items << { item: next_item[:item].join_expr.rarg, type: next_item[:type] }
286
+ subselect_items << next_item[:item].join_expr.quals
287
+ when :row_expr
288
+ from_clause_items += next_item[:item].row_expr.args.map { |a| { item: a, type: next_item[:type] } }
289
+ when :range_var
290
+ rangevar = next_item[:item].range_var
291
+ next if rangevar.schemaname.empty? && @cte_names.include?(rangevar.relname)
292
+
293
+ table = [rangevar.schemaname, rangevar.relname].reject { |s| s.nil? || s.empty? }.join('.')
294
+ @tables << {
295
+ name: table,
296
+ type: next_item[:type],
297
+ location: rangevar.location,
298
+ schemaname: (rangevar.schemaname unless rangevar.schemaname.empty?),
299
+ relname: rangevar.relname,
300
+ inh: rangevar.inh
301
+ }
302
+ @aliases[rangevar.alias.aliasname] = table if rangevar.alias
303
+ when :range_subselect
304
+ statements << next_item[:item].range_subselect.subquery
305
+ when :range_function
306
+ subselect_items += next_item[:item].range_function.functions
307
+ end
244
308
  end
309
+
310
+ break if subselect_items.empty? && statements.empty? && from_clause_items.empty?
245
311
  end
246
312
 
247
313
  @tables.uniq!