pg_query 2.0.1 → 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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +220 -114
  3. data/README.md +12 -0
  4. data/Rakefile +6 -21
  5. data/ext/pg_query/extconf.rb +5 -2
  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 +10 -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 +33 -21
  24. data/ext/pg_query/pg_query_fingerprint.c +123 -33
  25. data/ext/pg_query/pg_query_fingerprint.h +3 -1
  26. data/ext/pg_query/pg_query_normalize.c +222 -61
  27. data/ext/pg_query/pg_query_parse_plpgsql.c +21 -1
  28. data/ext/pg_query/pg_query_ruby.sym +1 -0
  29. data/ext/pg_query/protobuf-c.c +34 -27
  30. data/ext/pg_query/src_backend_utils_mmgr_mcxt.c +36 -0
  31. data/ext/pg_query/src_common_hashfn.c +420 -0
  32. data/ext/pg_query/src_pl_plpgsql_src_pl_gram.c +1 -1
  33. data/lib/pg_query/filter_columns.rb +3 -1
  34. data/lib/pg_query/fingerprint.rb +1 -3
  35. data/lib/pg_query/parse.rb +101 -46
  36. data/lib/pg_query/pg_query_pb.rb +1385 -1383
  37. data/lib/pg_query/truncate.rb +12 -4
  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
@@ -4,8 +4,8 @@ module PgQuery
4
4
 
5
5
  begin
6
6
  result = PgQuery::ParseResult.decode(result)
7
- rescue Google::Protobuf::ParseError
8
- raise PgQuery::ParseError.new('Failed to parse tree', __FILE__, __LINE__, -1)
7
+ rescue Google::Protobuf::ParseError => e
8
+ raise PgQuery::ParseError.new(format('Failed to parse tree: %s', e.message), __FILE__, __LINE__, -1)
9
9
  end
10
10
 
11
11
  warnings = []
@@ -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,20 +112,16 @@ 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
126
  when :SETOP_UNION
105
127
  statements << PgQuery::Node.new(select_stmt: statement.select_stmt.larg) if statement.select_stmt.larg
@@ -117,7 +139,13 @@ 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
 
@@ -168,6 +196,10 @@ module PgQuery
168
196
  @tables += objects.map { |r| { name: r.join('.'), type: :ddl } }
169
197
  when :OBJECT_RULE, :OBJECT_TRIGGER
170
198
  @tables += objects.map { |r| { name: r[0..-2].join('.'), type: :ddl } }
199
+ when :OBJECT_FUNCTION
200
+ # Only one function can be dropped in a statement
201
+ obj = statement.drop_stmt.objects[0].object_with_args
202
+ @functions << { function: obj.objname[0].string.str, type: :ddl }
171
203
  end
172
204
  when :grant_stmt
173
205
  objects = statement.grant_stmt.objects
@@ -184,12 +216,28 @@ module PgQuery
184
216
  # The following are other statements that don't fit into query/DML/DDL
185
217
  when :explain_stmt
186
218
  statements << statement.explain_stmt.query
219
+ when :create_function_stmt
220
+ @functions << {
221
+ function: statement.create_function_stmt.funcname[0].string.str,
222
+ type: :ddl
223
+ }
224
+ when :rename_stmt
225
+ if statement.rename_stmt.rename_type == :OBJECT_FUNCTION
226
+ original_name = statement.rename_stmt.object.object_with_args.objname[0].string.str
227
+ new_name = statement.rename_stmt.newname
228
+ @functions += [
229
+ { function: original_name, type: :ddl },
230
+ { function: new_name, type: :ddl }
231
+ ]
232
+ end
187
233
  end
188
234
  end
189
235
 
190
236
  next_item = subselect_items.shift
191
237
  if next_item
192
238
  case next_item.node
239
+ when :list
240
+ subselect_items += next_item.list.items
193
241
  when :a_expr
194
242
  %w[lexpr rexpr].each do |side|
195
243
  elem = next_item.a_expr.public_send(side)
@@ -201,47 +249,54 @@ module PgQuery
201
249
  end
202
250
  end
203
251
  when :bool_expr
204
- subselect_items.concat(next_item.bool_expr.args)
252
+ subselect_items.concat(next_item.bool_expr.args.to_ary)
253
+ when :coalesce_expr
254
+ subselect_items.concat(next_item.coalesce_expr.args.to_ary)
255
+ when :min_max_expr
256
+ subselect_items.concat(next_item.min_max_expr.args.to_ary)
205
257
  when :res_target
206
258
  subselect_items << next_item.res_target.val
207
259
  when :sub_link
208
260
  statements << next_item.sub_link.subselect
261
+ when :func_call
262
+ subselect_items.concat(next_item.func_call.args.to_ary)
263
+ @functions << {
264
+ function: next_item.func_call.funcname.map { |f| f.string.str }.join('.'),
265
+ type: :call
266
+ }
209
267
  end
210
268
  end
211
269
 
212
- break if subselect_items.empty? && statements.empty?
213
- end
214
-
215
- loop do
216
270
  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
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
244
297
  end
298
+
299
+ break if subselect_items.empty? && statements.empty? && from_clause_items.empty?
245
300
  end
246
301
 
247
302
  @tables.uniq!