pg_query 2.0.1 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
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!