arel_toolkit 0.2.0 → 0.4.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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +3 -0
  3. data/.github/workflows/develop.yml +90 -0
  4. data/.github/workflows/master.yml +67 -0
  5. data/.gitignore +8 -0
  6. data/.rubocop.yml +13 -2
  7. data/Appraisals +13 -0
  8. data/CHANGELOG.md +132 -6
  9. data/Gemfile +5 -0
  10. data/Gemfile.lock +92 -12
  11. data/Guardfile +23 -12
  12. data/README.md +104 -6
  13. data/Rakefile +18 -0
  14. data/arel_toolkit.gemspec +19 -4
  15. data/benchmark.rb +54 -0
  16. data/bin/console +1 -0
  17. data/ext/pg_result_init/extconf.rb +52 -0
  18. data/ext/pg_result_init/pg_result_init.c +138 -0
  19. data/ext/pg_result_init/pg_result_init.h +6 -0
  20. data/gemfiles/active_record_6.gemfile +7 -0
  21. data/gemfiles/active_record_6.gemfile.lock +210 -0
  22. data/gemfiles/arel_gems.gemfile +10 -0
  23. data/gemfiles/arel_gems.gemfile.lock +284 -0
  24. data/gemfiles/default.gemfile +5 -0
  25. data/gemfiles/default.gemfile.lock +208 -0
  26. data/lib/arel/enhance.rb +17 -0
  27. data/lib/arel/enhance/context_enhancer/arel_table.rb +92 -0
  28. data/lib/arel/enhance/node.rb +232 -0
  29. data/lib/arel/enhance/path.rb +38 -0
  30. data/lib/arel/enhance/path_node.rb +26 -0
  31. data/lib/arel/enhance/query.rb +38 -0
  32. data/lib/arel/enhance/query_methods.rb +23 -0
  33. data/lib/arel/enhance/visitor.rb +97 -0
  34. data/lib/arel/extensions.rb +55 -3
  35. data/lib/arel/extensions/active_model_attribute_with_cast_value.rb +22 -0
  36. data/lib/arel/extensions/active_record_relation_query_attribute.rb +22 -0
  37. data/lib/arel/extensions/active_record_type_caster_connection.rb +7 -0
  38. data/lib/arel/extensions/active_record_type_caster_map.rb +7 -0
  39. data/lib/arel/extensions/array.rb +2 -9
  40. data/lib/arel/extensions/assignment.rb +22 -0
  41. data/lib/arel/extensions/at_time_zone.rb +37 -0
  42. data/lib/arel/extensions/attributes_attribute.rb +47 -0
  43. data/lib/arel/extensions/binary.rb +7 -0
  44. data/lib/arel/extensions/bind_param.rb +15 -0
  45. data/lib/arel/extensions/bit_string.rb +2 -9
  46. data/lib/arel/extensions/case.rb +17 -0
  47. data/lib/arel/extensions/coalesce.rb +17 -3
  48. data/lib/arel/extensions/conflict.rb +9 -0
  49. data/lib/arel/extensions/contained_within_equals.rb +10 -0
  50. data/lib/arel/extensions/contains.rb +27 -5
  51. data/lib/arel/extensions/contains_equals.rb +10 -0
  52. data/lib/arel/extensions/current_catalog.rb +4 -0
  53. data/lib/arel/extensions/current_date.rb +4 -0
  54. data/lib/arel/extensions/current_of_expression.rb +2 -9
  55. data/lib/arel/extensions/current_role.rb +4 -0
  56. data/lib/arel/extensions/current_row.rb +7 -0
  57. data/lib/arel/extensions/current_schema.rb +4 -0
  58. data/lib/arel/extensions/current_user.rb +4 -0
  59. data/lib/arel/extensions/dealocate.rb +31 -0
  60. data/lib/arel/extensions/default_values.rb +4 -0
  61. data/lib/arel/extensions/delete_manager.rb +25 -0
  62. data/lib/arel/extensions/delete_statement.rb +32 -8
  63. data/lib/arel/extensions/distinct_from.rb +3 -16
  64. data/lib/arel/extensions/dot.rb +11 -0
  65. data/lib/arel/extensions/equality.rb +2 -4
  66. data/lib/arel/extensions/exists.rb +59 -0
  67. data/lib/arel/extensions/extract_from.rb +25 -0
  68. data/lib/arel/extensions/factorial.rb +10 -2
  69. data/lib/arel/extensions/false.rb +7 -0
  70. data/lib/arel/extensions/function.rb +44 -14
  71. data/lib/arel/extensions/greatest.rb +17 -3
  72. data/lib/arel/extensions/indirection.rb +3 -12
  73. data/lib/arel/extensions/infer.rb +7 -7
  74. data/lib/arel/extensions/infix_operation.rb +17 -0
  75. data/lib/arel/extensions/insert_manager.rb +21 -0
  76. data/lib/arel/extensions/insert_statement.rb +35 -9
  77. data/lib/arel/extensions/into.rb +21 -0
  78. data/lib/arel/extensions/json_get_field.rb +10 -0
  79. data/lib/arel/extensions/json_get_object.rb +10 -0
  80. data/lib/arel/extensions/json_path_get_field.rb +10 -0
  81. data/lib/arel/extensions/json_path_get_object.rb +10 -0
  82. data/lib/arel/extensions/jsonb_all_key_exists.rb +10 -0
  83. data/lib/arel/extensions/jsonb_any_key_exists.rb +10 -0
  84. data/lib/arel/extensions/jsonb_key_exists.rb +10 -0
  85. data/lib/arel/extensions/least.rb +17 -3
  86. data/lib/arel/extensions/named_argument.rb +24 -0
  87. data/lib/arel/extensions/named_function.rb +7 -0
  88. data/lib/arel/extensions/node.rb +10 -0
  89. data/lib/arel/extensions/not_distinct_from.rb +3 -16
  90. data/lib/arel/extensions/not_equal.rb +2 -4
  91. data/lib/arel/extensions/ordering.rb +21 -6
  92. data/lib/arel/extensions/overlap.rb +1 -1
  93. data/lib/arel/extensions/overlaps.rb +49 -0
  94. data/lib/arel/extensions/overlay.rb +53 -0
  95. data/lib/arel/extensions/position.rb +27 -0
  96. data/lib/arel/extensions/prepare.rb +39 -0
  97. data/lib/arel/extensions/range_function.rb +10 -2
  98. data/lib/arel/extensions/row.rb +3 -8
  99. data/lib/arel/extensions/select_core.rb +73 -0
  100. data/lib/arel/extensions/select_manager.rb +25 -0
  101. data/lib/arel/extensions/select_statement.rb +31 -9
  102. data/lib/arel/extensions/session_user.rb +4 -0
  103. data/lib/arel/extensions/set_to_default.rb +4 -0
  104. data/lib/arel/extensions/substring.rb +46 -0
  105. data/lib/arel/extensions/table.rb +43 -10
  106. data/lib/arel/extensions/time_with_precision.rb +6 -0
  107. data/lib/arel/extensions/to_sql.rb +27 -0
  108. data/lib/arel/extensions/top.rb +8 -0
  109. data/lib/arel/extensions/transaction.rb +45 -0
  110. data/lib/arel/extensions/tree_manager.rb +15 -0
  111. data/lib/arel/extensions/trim.rb +44 -0
  112. data/lib/arel/extensions/true.rb +7 -0
  113. data/lib/arel/extensions/type_cast.rb +11 -0
  114. data/lib/arel/extensions/unary.rb +7 -0
  115. data/lib/arel/extensions/unary_operation.rb +16 -0
  116. data/lib/arel/extensions/unknown.rb +4 -0
  117. data/lib/arel/extensions/update_manager.rb +25 -0
  118. data/lib/arel/extensions/update_statement.rb +31 -6
  119. data/lib/arel/extensions/user.rb +4 -0
  120. data/lib/arel/extensions/values_list.rb +15 -0
  121. data/lib/arel/extensions/variable_set.rb +55 -0
  122. data/lib/arel/extensions/variable_show.rb +26 -0
  123. data/lib/arel/middleware.rb +27 -0
  124. data/lib/arel/middleware/active_record_extension.rb +13 -0
  125. data/lib/arel/middleware/cache_accessor.rb +35 -0
  126. data/lib/arel/middleware/chain.rb +172 -0
  127. data/lib/arel/middleware/database_executor.rb +77 -0
  128. data/lib/arel/middleware/no_op_cache.rb +9 -0
  129. data/lib/arel/middleware/postgresql_adapter.rb +62 -0
  130. data/lib/arel/middleware/railtie.rb +25 -0
  131. data/lib/arel/middleware/result.rb +170 -0
  132. data/lib/arel/middleware/to_sql_executor.rb +15 -0
  133. data/lib/arel/middleware/to_sql_middleware.rb +33 -0
  134. data/lib/arel/sql_to_arel.rb +8 -4
  135. data/lib/arel/sql_to_arel/error.rb +6 -0
  136. data/lib/arel/sql_to_arel/pg_query_visitor.rb +324 -76
  137. data/lib/arel/sql_to_arel/pg_query_visitor/frame_options.rb +112 -0
  138. data/lib/arel/sql_to_arel/result.rb +30 -0
  139. data/lib/arel/transformer.rb +8 -0
  140. data/lib/arel/transformer/prefix_schema_name.rb +183 -0
  141. data/lib/arel/transformer/remove_active_record_info.rb +40 -0
  142. data/lib/arel/transformer/replace_table_with_subquery.rb +31 -0
  143. data/lib/arel_toolkit.rb +16 -1
  144. data/lib/arel_toolkit/version.rb +1 -1
  145. metadata +278 -25
  146. data/.travis.yml +0 -21
  147. data/lib/arel/extensions/generate_series.rb +0 -9
  148. data/lib/arel/extensions/rank.rb +0 -9
  149. data/lib/arel/sql_to_arel/frame_options.rb +0 -110
  150. data/lib/arel/sql_to_arel/unbound_column_reference.rb +0 -5
@@ -0,0 +1,77 @@
1
+ module Arel
2
+ module Middleware
3
+ class DatabaseExecutor
4
+ attr_reader :middleware
5
+
6
+ attr_accessor :index
7
+ attr_reader :context
8
+ attr_reader :final_block
9
+
10
+ def initialize(middleware)
11
+ @middleware = middleware
12
+ end
13
+
14
+ def run(arel, context, final_block)
15
+ @index = 0
16
+ @context = context
17
+ @final_block = final_block
18
+
19
+ result = call(arel)
20
+ check_return_type result
21
+ result
22
+ ensure
23
+ @index = 0
24
+ @context = nil
25
+ @final_block = nil
26
+ end
27
+
28
+ def call(next_arel)
29
+ check_argument_type next_arel
30
+
31
+ current_middleware = middleware[index]
32
+
33
+ return execute_sql(next_arel) if current_middleware.nil?
34
+
35
+ self.index += 1
36
+
37
+ case current_middleware.method(:call).arity
38
+ when 2
39
+ current_middleware.call(next_arel, self)
40
+ else
41
+ current_middleware.call(next_arel, self, context.dup)
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def execute_sql(next_arel)
48
+ sql, binds = next_arel.to_sql_and_binds
49
+
50
+ context[:cache_accessor].write(
51
+ transformed_sql: sql,
52
+ transformed_binds: binds,
53
+ original_sql: context[:original_sql],
54
+ original_binds: context[:original_binds],
55
+ )
56
+
57
+ sql_result = final_block.call(sql, binds)
58
+
59
+ check_return_type sql_result
60
+ sql_result
61
+ end
62
+
63
+ def check_argument_type(next_arel)
64
+ return if next_arel.is_a?(Arel::Enhance::Node)
65
+
66
+ raise "Only `Arel::Enhance::Node` is valid for middleware, passed `#{next_arel.class}`"
67
+ end
68
+
69
+ def check_return_type(return_object)
70
+ return if return_object.is_a?(Arel::Middleware::Result)
71
+
72
+ raise 'Object returned from middleware needs to be wrapped in `Arel::Middleware::Result`' \
73
+ " for object `#{return_object}`"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,9 @@
1
+ module Arel
2
+ module Middleware
3
+ module NoOpCache
4
+ def self.read(key); end
5
+
6
+ def self.write(key, sql); end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,62 @@
1
+ module Arel
2
+ module Middleware
3
+ module PostgreSQLAdapter
4
+ def initialize(*args)
5
+ Arel.middleware.none do
6
+ super(*args)
7
+ end
8
+ end
9
+
10
+ def execute(sql, name = nil)
11
+ super(sql, name)
12
+ end
13
+
14
+ alias parent_execute execute
15
+
16
+ # rubocop:disable Lint/DuplicateMethods
17
+ def execute(sql, name = nil)
18
+ Arel::Middleware.current_chain.execute(sql) do |processed_sql|
19
+ Arel::Middleware::Result.create(
20
+ data: parent_execute(processed_sql, name),
21
+ from: Arel::Middleware::PGResult,
22
+ to: Arel::Middleware::PGResult,
23
+ )
24
+ end
25
+ end
26
+ # rubocop:enable Lint/DuplicateMethods
27
+
28
+ def query(sql, name = nil)
29
+ Arel::Middleware.current_chain.execute(sql) do |processed_sql|
30
+ # NOTE: we're not calling `super` here, but execute.
31
+ # The `query` super does not return the columns, like the other methods.
32
+ # As we want the result objects to be the same, we call execute instead.
33
+ Arel::Middleware::Result.create(
34
+ data: parent_execute(processed_sql, name),
35
+ from: Arel::Middleware::PGResult,
36
+ to: Arel::Middleware::ArrayResult,
37
+ )
38
+ end
39
+ end
40
+
41
+ def exec_no_cache(sql, name, binds)
42
+ Arel::Middleware.current_chain.execute(sql, binds) do |processed_sql, processed_binds|
43
+ Arel::Middleware::Result.create(
44
+ data: super(processed_sql, name, processed_binds),
45
+ from: Arel::Middleware::PGResult,
46
+ to: Arel::Middleware::PGResult,
47
+ )
48
+ end
49
+ end
50
+
51
+ def exec_cache(sql, name, binds)
52
+ Arel::Middleware.current_chain.execute(sql, binds) do |processed_sql, processed_binds|
53
+ Arel::Middleware::Result.create(
54
+ data: super(processed_sql, name, processed_binds),
55
+ from: Arel::Middleware::PGResult,
56
+ to: Arel::Middleware::PGResult,
57
+ )
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,25 @@
1
+ module Arel
2
+ module Middleware
3
+ if defined? Rails::Railtie
4
+ class Railtie < Rails::Railtie
5
+ initializer 'arel.middleware.insert' do
6
+ ActiveSupport.on_load :active_record do
7
+ Arel::Middleware::Railtie.insert
8
+ end
9
+ end
10
+ end
11
+ end
12
+
13
+ class Railtie
14
+ def self.insert
15
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(
16
+ Arel::Middleware::PostgreSQLAdapter,
17
+ )
18
+
19
+ ActiveRecord::Base.singleton_class.prepend(
20
+ Arel::Middleware::ActiveRecordExtension,
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,170 @@
1
+ module Arel
2
+ module Middleware
3
+ class Column
4
+ attr_reader :name
5
+ attr_reader :metadata
6
+
7
+ def initialize(name, metadata)
8
+ @name = name
9
+ @metadata = metadata
10
+ end
11
+ end
12
+
13
+ # Class is very similar to ActiveRecord::Result
14
+ # activerecord/lib/active_record/result.rb
15
+ class Result
16
+ attr_reader :original_data
17
+
18
+ def self.create(from:, to:, data:)
19
+ Result.new from, to, data
20
+ end
21
+
22
+ def initialize(from_caster, to_caster, original_data)
23
+ @from_caster = from_caster
24
+ @to_caster = to_caster
25
+ @original_data = original_data
26
+ @modified = false
27
+ end
28
+
29
+ def columns
30
+ @columns ||= column_objects.map(&:name)
31
+ end
32
+
33
+ def column_objects
34
+ @column_objects ||= from_caster.column_objects(original_data)
35
+ end
36
+
37
+ def rows
38
+ @rows ||= from_caster.rows(original_data)
39
+ end
40
+
41
+ def remove_column(column_name)
42
+ column_index = columns.index(column_name)
43
+ raise "Unknown column `#{column_name}`. Existing columns: `#{columns}`" if column_index.nil?
44
+
45
+ @hash_rows = nil
46
+ @columns = nil
47
+ @modified = true
48
+
49
+ column_objects.delete_at(column_index)
50
+ deleted_rows = []
51
+
52
+ rows.map! do |row|
53
+ deleted_rows << row.delete_at(column_index)
54
+ row
55
+ end
56
+
57
+ deleted_rows
58
+ end
59
+
60
+ def hash_rows
61
+ @hash_rows ||=
62
+ begin
63
+ rows.map do |row|
64
+ hash = {}
65
+
66
+ index = 0
67
+ length = columns.length
68
+
69
+ while index < length
70
+ hash[columns[index]] = row[index]
71
+ index += 1
72
+ end
73
+
74
+ hash
75
+ end
76
+ end
77
+ end
78
+
79
+ def to_casted_result
80
+ to_caster.cast_to(self)
81
+ end
82
+
83
+ def modified?
84
+ @modified
85
+ end
86
+
87
+ private
88
+
89
+ attr_reader :to_caster, :from_caster
90
+ end
91
+
92
+ class PGResult
93
+ class << self
94
+ def column_objects(pg_result)
95
+ pg_result.fields.each_with_index.map do |field, index|
96
+ Column.new(
97
+ field,
98
+ tableid: pg_result.ftable(index),
99
+ columnid: pg_result.ftablecol(index),
100
+ format: pg_result.fformat(index),
101
+ typid: pg_result.ftype(index),
102
+ typlen: pg_result.fsize(index),
103
+ atttypmod: pg_result.fmod(index),
104
+ )
105
+ end
106
+ end
107
+
108
+ def rows(data)
109
+ data.values
110
+ end
111
+
112
+ def cast_to(result)
113
+ return result.original_data unless result.modified?
114
+
115
+ pg_columns = result_to_columns(result)
116
+ conn = ActiveRecord::Base.connection.raw_connection
117
+ new_result = PgResultInit.create(conn, result.original_data, pg_columns, result.rows)
118
+ result.original_data.clear
119
+ new_result
120
+ end
121
+
122
+ private
123
+
124
+ def result_to_columns(result)
125
+ result.column_objects.map do |column|
126
+ {
127
+ name: column.name,
128
+ tableid: column.metadata.fetch(:tableid, 0),
129
+ columnid: column.metadata.fetch(:columnid, 0),
130
+ format: column.metadata.fetch(:format, 0),
131
+ typid: column.metadata.fetch(:typid),
132
+ typlen: column.metadata.fetch(:typlen),
133
+ atttypmod: column.metadata.fetch(:atttypmod, -1),
134
+ }
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ class EmptyPGResult < PGResult
141
+ class << self
142
+ def cast_to(_result)
143
+ ActiveRecord::Base.connection.raw_connection.make_empty_pgresult(2)
144
+ end
145
+ end
146
+ end
147
+
148
+ class ArrayResult
149
+ def self.cast_to(result)
150
+ result.rows
151
+ end
152
+ end
153
+
154
+ class StringResult
155
+ class << self
156
+ def column_objects(_string)
157
+ []
158
+ end
159
+
160
+ def rows(_string)
161
+ []
162
+ end
163
+
164
+ def cast_to(result)
165
+ result.original_data
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,15 @@
1
+ module Arel
2
+ module Middleware
3
+ class ToSqlExecutor < DatabaseExecutor
4
+ private
5
+
6
+ def execute_sql(next_arel)
7
+ Arel::Middleware::Result.create(
8
+ data: next_arel.to_sql,
9
+ from: Arel::Middleware::StringResult,
10
+ to: Arel::Middleware::EmptyPGResult,
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,33 @@
1
+ module Arel
2
+ module Middleware
3
+ class ToSqlMiddleware
4
+ attr_reader :sql, :type, :query_class
5
+
6
+ def initialize(type)
7
+ @sql = []
8
+ @type = type
9
+ @query_class = class_from_type
10
+ end
11
+
12
+ def call(next_arel, next_middleware)
13
+ sql << next_arel.to_sql unless next_arel.query(class: query_class).empty?
14
+ next_middleware.call(next_arel)
15
+ end
16
+
17
+ private
18
+
19
+ def class_from_type
20
+ case type
21
+ when :insert
22
+ Arel::Nodes::InsertStatement
23
+ when :select
24
+ Arel::Nodes::SelectStatement
25
+ when :update
26
+ Arel::Nodes::UpdateStatement
27
+ when :delete
28
+ Arel::Nodes::DeleteStatement
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,8 +1,12 @@
1
- require 'arel/sql_to_arel/pg_query_visitor'
2
- require 'arel/sql_to_arel/unbound_column_reference'
1
+ require_relative './sql_to_arel/result'
2
+ require_relative './sql_to_arel/error'
3
+ require_relative './sql_to_arel/pg_query_visitor'
3
4
 
4
5
  module Arel
5
- def self.sql_to_arel(sql)
6
- SqlToArel::PgQueryVisitor.new.accept(sql)
6
+ module SqlToArel
7
+ end
8
+
9
+ def self.sql_to_arel(sql, binds: [])
10
+ SqlToArel::PgQueryVisitor.new.accept(sql, binds)
7
11
  end
8
12
  end
@@ -0,0 +1,6 @@
1
+ module Arel
2
+ module SqlToArel
3
+ class Error < StandardError
4
+ end
5
+ end
6
+ end
@@ -6,7 +6,7 @@
6
6
  # rubocop:disable Metrics/ParameterLists
7
7
 
8
8
  require 'pg_query'
9
- require_relative './frame_options'
9
+ require_relative './pg_query_visitor/frame_options'
10
10
 
11
11
  module Arel
12
12
  module SqlToArel
@@ -15,13 +15,24 @@ module Arel
15
15
  MIN_MAX_EXPR = 'MinMaxExpr'.freeze
16
16
 
17
17
  attr_reader :object
18
+ attr_reader :binds
19
+ attr_reader :sql
18
20
 
19
- def accept(sql)
21
+ def accept(sql, binds = [])
20
22
  tree = PgQuery.parse(sql).tree
21
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/33' if tree.length > 1
22
23
 
23
- @object = tree.first
24
- visit object, :top
24
+ @object = tree
25
+ @binds = binds
26
+ @sql = sql
27
+
28
+ Result.new visit(object, :top)
29
+ rescue ::PgQuery::ParseError => e
30
+ new_error = ::PgQuery::ParseError.new(e.message, __FILE__, __LINE__, -1)
31
+ raise new_error, e.message, e.backtrace
32
+ rescue ::StandardError => e
33
+ raise e.class, e.message, e.backtrace if e.is_a?(Arel::SqlToArel::Error)
34
+
35
+ boom e.message, e.backtrace
25
36
  end
26
37
 
27
38
  private
@@ -72,7 +83,7 @@ module Arel
72
83
  Arel::Nodes::NullIf.new(left, right)
73
84
 
74
85
  when PgQuery::AEXPR_OF
75
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/34'
86
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/34'
76
87
 
77
88
  when PgQuery::AEXPR_IN
78
89
  left = visit(lexpr)
@@ -91,7 +102,7 @@ module Arel
91
102
  escape = nil
92
103
 
93
104
  if right.is_a?(Array)
94
- raise "Don't know how to handle length `#{right.length}`" if right.length != 2
105
+ boom "Don't know how to handle length `#{right.length}`" if right.length != 2
95
106
 
96
107
  right, escape = right
97
108
  end
@@ -110,7 +121,7 @@ module Arel
110
121
  escape = nil
111
122
 
112
123
  if right.is_a?(Array)
113
- raise "Don't know how to handle length `#{right.length}`" if right.length != 2
124
+ boom "Don't know how to handle length `#{right.length}`" if right.length != 2
114
125
 
115
126
  right, escape = right
116
127
  end
@@ -129,7 +140,7 @@ module Arel
129
140
  escape = nil
130
141
 
131
142
  if right.is_a?(Array)
132
- raise "Don't know how to handle length `#{right.length}`" if right.length != 2
143
+ boom "Don't know how to handle length `#{right.length}`" if right.length != 2
133
144
 
134
145
  right, escape = right
135
146
  end
@@ -164,10 +175,10 @@ module Arel
164
175
  Arel::Nodes::NotBetweenSymmetric.new left, Arel::Nodes::And.new(right)
165
176
 
166
177
  when PgQuery::AEXPR_PAREN
167
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/35'
178
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/35'
168
179
 
169
180
  else
170
- raise "Unknown Expr type `#{kind}`"
181
+ boom "Unknown Expr type `#{kind}`"
171
182
  end
172
183
  end
173
184
 
@@ -176,7 +187,7 @@ module Arel
176
187
  end
177
188
 
178
189
  def visit_A_Indirection(arg:, indirection:)
179
- Arel::Nodes::Indirection.new(visit(arg, :operator), visit(indirection, :operator))
190
+ Arel::Nodes::Indirection.new(visit(arg), visit(indirection, :operator))
180
191
  end
181
192
 
182
193
  def visit_A_Star
@@ -184,7 +195,7 @@ module Arel
184
195
  end
185
196
 
186
197
  def visit_Alias(aliasname:)
187
- aliasname
198
+ Arel.sql visit_String(nil, str: aliasname)
188
199
  end
189
200
 
190
201
  def visit_BitString(str:)
@@ -205,7 +216,7 @@ module Arel
205
216
  Arel::Nodes::Not.new(args)
206
217
 
207
218
  else
208
- raise "? Boolop -> #{boolop}"
219
+ boom "? Boolop -> #{boolop}"
209
220
  end
210
221
 
211
222
  if context
@@ -238,7 +249,7 @@ module Arel
238
249
  Arel::Nodes::NotEqual.new(arg, Arel::Nodes::Unknown.new)
239
250
 
240
251
  else
241
- raise '?'
252
+ boom '?'
242
253
  end
243
254
  end
244
255
 
@@ -269,8 +280,21 @@ module Arel
269
280
  Arel::Nodes::Coalesce.new args
270
281
  end
271
282
 
272
- def visit_ColumnRef(context = nil, fields:)
273
- UnboundColumnReference.new visit(fields, context).join('.')
283
+ def visit_ColumnRef(fields:)
284
+ fields = fields.reverse
285
+ column = visit(fields[0], :operator)
286
+ table = visit(fields[1], :operator) if fields[1]
287
+ schema_name = visit(fields[2], :operator) if fields[2]
288
+ database = visit(fields[3], :operator) if fields[3]
289
+
290
+ table = Arel::Table.new(table) if table
291
+ attribute = Arel::Attribute.new(table, column)
292
+ attribute.schema_name = schema_name
293
+ attribute.database = database
294
+
295
+ return attribute if table
296
+
297
+ Arel::Nodes::UnqualifiedColumn.new Arel::Attribute.new(nil, column)
274
298
  end
275
299
 
276
300
  def visit_CommonTableExpr(ctename:, ctequery:)
@@ -283,6 +307,15 @@ module Arel
283
307
  Arel::Nodes::CurrentOfExpression.new(cursor_name)
284
308
  end
285
309
 
310
+ def visit_DefElem(defname:, arg:, defaction:)
311
+ case defname
312
+ when 'savepoint_name'
313
+ visit(arg)
314
+ else
315
+ boom "Unknown defname `#{defname}` with defaction `#{defaction}`"
316
+ end
317
+ end
318
+
286
319
  def visit_DeleteStmt(
287
320
  relation:,
288
321
  using_clause: nil,
@@ -332,15 +365,9 @@ module Arel
332
365
  when ['sum']
333
366
  Arel::Nodes::Sum.new args
334
367
 
335
- when ['rank']
336
- Arel::Nodes::Rank.new args
337
-
338
368
  when ['count']
339
369
  Arel::Nodes::Count.new args
340
370
 
341
- when ['generate_series']
342
- Arel::Nodes::GenerateSeries.new args
343
-
344
371
  when ['max']
345
372
  Arel::Nodes::Max.new args
346
373
 
@@ -356,10 +383,55 @@ module Arel
356
383
  when [PG_CATALOG, 'similar_escape']
357
384
  args
358
385
 
359
- else
360
- raise "Don't know how to handle `#{function_names}`" if function_names.length > 1
386
+ when [PG_CATALOG, 'date_part']
387
+ field, expression = args
388
+ [Arel::Nodes::ExtractFrom.new(expression, field)]
389
+
390
+ when [PG_CATALOG, 'timezone']
391
+ timezone, expression = args
392
+
393
+ [Arel::Nodes::AtTimeZone.new(maybe_add_grouping(expression), timezone)]
394
+
395
+ # https://www.postgresql.org/docs/10/functions-string.html
396
+ when [PG_CATALOG, 'position']
397
+ string, substring = args
398
+ [Arel::Nodes::Position.new(substring, string)]
399
+
400
+ when [PG_CATALOG, 'overlay']
401
+ string, substring, start, length = args
402
+ [Arel::Nodes::Overlay.new(string, substring, start, length)]
403
+
404
+ when [PG_CATALOG, 'ltrim']
405
+ string, substring = args
406
+ [Arel::Nodes::Trim.new('leading', substring, string)]
407
+
408
+ when [PG_CATALOG, 'rtrim']
409
+ string, substring = args
410
+ [Arel::Nodes::Trim.new('trailing', substring, string)]
411
+
412
+ when [PG_CATALOG, 'btrim']
413
+ string, substring = args
414
+ [Arel::Nodes::Trim.new('both', substring, string)]
415
+
416
+ when [PG_CATALOG, 'substring']
417
+ string, pattern, escape = args
418
+ [Arel::Nodes::Substring.new(string, pattern, escape)]
419
+
420
+ when [PG_CATALOG, 'overlaps']
421
+ start1, end1, start2, end2 = args
422
+ [Arel::Nodes::Overlaps.new(start1, end1, start2, end2)]
361
423
 
362
- Arel::Nodes::NamedFunction.new(function_names.first, args)
424
+ else
425
+ case function_names.length
426
+ when 2
427
+ func = Arel::Nodes::NamedFunction.new(function_names.last, args)
428
+ func.schema_name = function_names.first
429
+ func
430
+ when 1
431
+ Arel::Nodes::NamedFunction.new(function_names.first, args)
432
+ else
433
+ boom "Don't know how to handle function names length `#{function_names.length}`"
434
+ end
363
435
  end
364
436
 
365
437
  func.distinct = (agg_distinct.nil? ? false : true) unless func.is_a?(::Array)
@@ -376,17 +448,16 @@ module Arel
376
448
  end
377
449
 
378
450
  def visit_InferClause(conname: nil, index_elems: nil)
379
- infer = Arel::Nodes::Infer.new
380
- infer.name = Arel.sql(conname) if conname
381
- infer.indexes = visit(index_elems) if index_elems
382
- infer
451
+ left = Arel.sql(conname) if conname
452
+ right = visit(index_elems) if index_elems
453
+ Arel::Nodes::Infer.new left, right
383
454
  end
384
455
 
385
456
  def visit_IndexElem(name:, ordering:, nulls_ordering:)
386
- raise "Unknown ordering `#{ordering}`" unless ordering.zero?
387
- raise "Unknown nulls ordering `#{ordering}`" unless nulls_ordering.zero?
457
+ boom "Unknown ordering `#{ordering}`" unless ordering.zero?
458
+ boom "Unknown nulls ordering `#{ordering}`" unless nulls_ordering.zero?
388
459
 
389
- Arel.sql(name)
460
+ Arel.sql visit_String(str: name)
390
461
  end
391
462
 
392
463
  def visit_InsertStmt(
@@ -418,7 +489,7 @@ module Arel
418
489
  end
419
490
 
420
491
  insert_statement.returning = visit(returning_list, :select)
421
- insert_statement.on_conflict = visit(on_conflict_clause) if on_conflict_clause
492
+ insert_statement.conflict = visit(on_conflict_clause) if on_conflict_clause
422
493
  insert_manager
423
494
  end
424
495
 
@@ -426,6 +497,12 @@ module Arel
426
497
  ival
427
498
  end
428
499
 
500
+ def visit_IntoClause(rel:, on_commit:)
501
+ raise "Unknown on_commit `#{on_commit}`" unless on_commit.zero?
502
+
503
+ Arel::Nodes::Into.new(visit(rel))
504
+ end
505
+
429
506
  def visit_JoinExpr(jointype:, is_natural: nil, larg:, rarg:, quals: nil)
430
507
  join_class = case jointype
431
508
  when 0
@@ -463,12 +540,12 @@ module Arel
463
540
  1 => 'FOR KEY SHARE',
464
541
  2 => 'FOR SHARE',
465
542
  3 => 'FOR NO KEY UPDATE',
466
- 4 => 'FOR UPDATE'
543
+ 4 => 'FOR UPDATE',
467
544
  }.fetch(strength)
468
545
  wait_policy_clause = {
469
546
  0 => '',
470
547
  1 => ' SKIP LOCKED',
471
- 2 => ' NOWAIT'
548
+ 2 => ' NOWAIT',
472
549
  }.fetch(wait_policy)
473
550
 
474
551
  Arel::Nodes::Lock.new Arel.sql("#{strength_clause}#{wait_policy_clause}")
@@ -481,10 +558,17 @@ module Arel
481
558
  when 1
482
559
  Arel::Nodes::Least.new visit(args)
483
560
  else
484
- raise "Unknown Op -> #{op}"
561
+ boom "Unknown Op -> #{op}"
485
562
  end
486
563
  end
487
564
 
565
+ def visit_NamedArgExpr(arg:, name:, argnumber:)
566
+ arg = visit(arg)
567
+ boom '' unless argnumber == -1
568
+
569
+ Arel::Nodes::NamedArgument.new(name, arg)
570
+ end
571
+
488
572
  def visit_Null(**_)
489
573
  Arel.sql 'NULL'
490
574
  end
@@ -509,29 +593,36 @@ module Arel
509
593
  conflict
510
594
  end
511
595
 
512
- def visit_ParamRef(_args)
513
- Arel::Nodes::BindParam.new(nil)
514
- end
596
+ def visit_ParamRef(number: nil)
597
+ value = (binds[number - 1] unless binds.empty?)
515
598
 
516
- def visit_RangeFunction(is_rowsfrom:, functions:, lateral: false, ordinality: false)
517
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/36' unless is_rowsfrom == true
599
+ Arel::Nodes::BindParam.new(value)
600
+ end
518
601
 
602
+ def visit_RangeFunction(
603
+ is_rowsfrom: nil,
604
+ functions:,
605
+ lateral: false,
606
+ ordinality: false,
607
+ aliaz: nil
608
+ )
519
609
  functions = functions.map do |function_array|
520
610
  function, empty_value = function_array
521
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/37' unless empty_value.nil?
611
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/37' unless empty_value.nil?
522
612
 
523
613
  visit(function)
524
614
  end
525
615
 
526
- node = Arel::Nodes::RangeFunction.new functions
616
+ node = Arel::Nodes::RangeFunction.new functions, is_rowsfrom: is_rowsfrom
527
617
  node = lateral ? Arel::Nodes::Lateral.new(node) : node
528
- ordinality ? Arel::Nodes::WithOrdinality.new(node) : node
618
+ node = ordinality ? Arel::Nodes::WithOrdinality.new(node) : node
619
+ aliaz.nil? ? node : Arel::Nodes::As.new(node, visit(aliaz))
529
620
  end
530
621
 
531
622
  def visit_RangeSubselect(aliaz:, subquery:, lateral: false)
532
623
  aliaz = visit(aliaz)
533
624
  subquery = visit(subquery)
534
- node = Arel::Nodes::TableAlias.new(Arel::Nodes::Grouping.new(subquery), aliaz)
625
+ node = Arel::Nodes::As.new(Arel::Nodes::Grouping.new(subquery), aliaz)
535
626
  lateral ? Arel::Nodes::Lateral.new(node) : node
536
627
  end
537
628
 
@@ -545,8 +636,8 @@ module Arel
545
636
  )
546
637
  end
547
638
 
548
- def visit_RawStmt(context, stmt:)
549
- visit(stmt, context)
639
+ def visit_RawStmt(context, **args)
640
+ visit(args.fetch(:stmt), context)
550
641
  end
551
642
 
552
643
  def visit_ResTarget(context, val: nil, name: nil)
@@ -555,19 +646,21 @@ module Arel
555
646
  val = visit(val)
556
647
 
557
648
  if name
558
- Arel::Nodes::As.new(val, Arel.sql(name))
649
+ aliaz = visit_Alias(aliasname: name)
650
+ Arel::Nodes::As.new(val, aliaz)
559
651
  else
560
652
  val
561
653
  end
562
654
  when :insert
563
655
  name
564
656
  when :update
565
- Arel::Nodes::Equality.new(
566
- Arel.sql(visit_String(str: name)),
567
- visit(val),
568
- )
657
+ relation = nil
658
+ column = Arel::Attribute.new(relation, name)
659
+ value = visit(val)
660
+
661
+ Nodes::Assignment.new(Nodes::UnqualifiedColumn.new(column), value)
569
662
  else
570
- raise "Unknown context `#{context}`"
663
+ boom "Unknown context `#{context}`"
571
664
  end
572
665
  end
573
666
 
@@ -591,6 +684,7 @@ module Arel
591
684
  op:,
592
685
  window_clause: nil,
593
686
  values_lists: nil,
687
+ into_clause: nil,
594
688
  all: nil,
595
689
  larg: nil,
596
690
  rarg: nil
@@ -600,14 +694,32 @@ module Arel
600
694
  select_statement = select_manager.ast
601
695
 
602
696
  froms, join_sources = generate_sources(from_clause)
697
+ if froms
698
+ froms = froms.first if froms.length == 1
699
+ select_core.froms = froms
700
+ end
701
+
603
702
  select_core.from = froms if froms
604
703
  select_core.source.right = join_sources
605
704
 
606
705
  select_core.projections = visit(target_list, :select) if target_list
607
- select_core.wheres = [visit(where_clause)] if where_clause
706
+
707
+ if where_clause
708
+ where_clause = visit(where_clause)
709
+ where_clause = if where_clause.is_a?(Arel::Nodes::And)
710
+ where_clause
711
+ else
712
+ Arel::Nodes::And.new([where_clause])
713
+ end
714
+
715
+ select_core.wheres = [where_clause]
716
+ end
717
+
608
718
  select_core.groups = visit(group_clause) if group_clause
609
719
  select_core.havings = [visit(having_clause)] if having_clause
610
720
  select_core.windows = visit(window_clause) if window_clause
721
+ select_core.into = visit(into_clause) if into_clause
722
+ select_core.top = ::Arel::Nodes::Top.new visit(limit_count) if limit_count
611
723
 
612
724
  if distinct_clause == [nil]
613
725
  select_core.set_quantifier = Arel::Nodes::Distinct.new
@@ -616,7 +728,7 @@ module Arel
616
728
  elsif distinct_clause.nil?
617
729
  select_core.set_quantifier = nil
618
730
  else
619
- raise "Unknown distinct clause `#{distinct_clause}`"
731
+ boom "Unknown distinct clause `#{distinct_clause}`"
620
732
  end
621
733
 
622
734
  select_statement.limit = ::Arel::Nodes::Limit.new visit(limit_count) if limit_count
@@ -632,12 +744,14 @@ module Arel
632
744
  value
633
745
  when Integer
634
746
  Arel.sql(value.to_s)
635
- when Arel::Nodes::TypeCast
747
+ when Arel::Nodes::TypeCast, Arel::Nodes::UnqualifiedColumn
636
748
  Arel.sql(value.to_sql)
637
749
  when Arel::Nodes::BindParam
638
750
  value
751
+ when Arel::Nodes::Quoted
752
+ value.value
639
753
  else
640
- raise "Unknown value `#{value}`"
754
+ boom "Unknown value `#{value}`"
641
755
  end
642
756
  end
643
757
  end
@@ -667,7 +781,7 @@ module Arel
667
781
  end
668
782
  else
669
783
  # https://www.postgresql.org/docs/10/queries-union.html
670
- raise "Unknown combining queries op `#{op}`"
784
+ boom "Unknown combining queries op `#{op}`"
671
785
  end
672
786
 
673
787
  unless union.nil?
@@ -723,7 +837,7 @@ module Arel
723
837
  when :operator
724
838
  str
725
839
  when :const
726
- Arel.sql "'#{str}'"
840
+ Arel::Nodes.build_quoted str
727
841
  else
728
842
  "\"#{str}\""
729
843
  end
@@ -735,7 +849,7 @@ module Arel
735
849
  operator = if oper_name
736
850
  operator = visit(oper_name, :operator)
737
851
  if operator.length > 1
738
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/39'
852
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/39'
739
853
  end
740
854
 
741
855
  operator.first
@@ -744,24 +858,51 @@ module Arel
744
858
  generate_sublink(sub_link_type, subselect, testexpr, operator)
745
859
  end
746
860
 
861
+ def visit_TransactionStmt(kind:, options: nil)
862
+ Arel::Nodes::Transaction.new(
863
+ kind,
864
+ (visit(options) if options),
865
+ )
866
+ end
867
+
747
868
  def visit_TypeCast(arg:, type_name:)
748
869
  arg = visit(arg)
749
870
  type_name = visit(type_name)
750
871
 
751
- Arel::Nodes::TypeCast.new(arg, type_name)
872
+ Arel::Nodes::TypeCast.new(maybe_add_grouping(arg), type_name)
752
873
  end
753
874
 
754
- def visit_TypeName(names:, typemod:)
875
+ def visit_TypeName(names:, typemod:, array_bounds: [])
876
+ array_bounds = visit(array_bounds)
877
+
755
878
  names = names.map do |name|
756
879
  visit(name, :operator)
757
880
  end
758
881
 
759
882
  names = names.reject { |name| name == PG_CATALOG }
760
883
 
761
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/40' if typemod != -1
762
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/41' if names.length > 1
884
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/40' if typemod != -1
885
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/41' if names.length > 1
886
+ if array_bounds != [] && array_bounds != [-1]
887
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/86'
888
+ end
763
889
 
764
- names.first
890
+ type_name = names.first
891
+ type_name = case type_name
892
+ when 'int4'
893
+ 'integer'
894
+ when 'float4'
895
+ 'real'
896
+ when 'float8'
897
+ 'double precision'
898
+ when 'timestamptz'
899
+ 'timestamp with time zone'
900
+ else
901
+ type_name
902
+ end
903
+
904
+ type_name << '[]' if array_bounds == [-1]
905
+ type_name
765
906
  end
766
907
 
767
908
  def visit_UpdateStmt(
@@ -786,6 +927,19 @@ module Arel
786
927
  update_manager
787
928
  end
788
929
 
930
+ def visit_VariableSetStmt(kind:, name:, args: [], is_local: false)
931
+ Arel::Nodes::VariableSet.new(
932
+ kind,
933
+ visit(args),
934
+ name,
935
+ is_local,
936
+ )
937
+ end
938
+
939
+ def visit_VariableShowStmt(name:)
940
+ Arel::Nodes::VariableShow.new(name)
941
+ end
942
+
789
943
  def visit_WindowDef(
790
944
  partition_clause: [],
791
945
  order_clause: [],
@@ -794,8 +948,11 @@ module Arel
794
948
  start_offset: nil,
795
949
  end_offset: nil
796
950
  )
797
- instance = name.nil? ? Arel::Nodes::Window.new : Arel::Nodes::NamedWindow.new(name)
951
+ if name.present? && partition_clause.empty? && order_clause.empty?
952
+ return Arel::Nodes::SqlLiteral.new(name)
953
+ end
798
954
 
955
+ instance = name.nil? ? Arel::Nodes::Window.new : Arel::Nodes::NamedWindow.new(name)
799
956
  instance.tap do |window|
800
957
  window.orders = visit order_clause
801
958
  window.partitions = visit partition_clause
@@ -819,13 +976,20 @@ module Arel
819
976
  end
820
977
 
821
978
  def generate_operator(left, right, operator)
979
+ left = maybe_add_grouping(left)
980
+ right = maybe_add_grouping(right)
981
+
822
982
  case operator
823
983
 
824
984
  # https://www.postgresql.org/docs/10/functions-math.html
825
985
  when '+'
826
986
  Arel::Nodes::Addition.new(left, right)
827
987
  when '-'
828
- Arel::Nodes::Subtraction.new(left, right)
988
+ if left.nil?
989
+ Arel::Nodes::UnaryOperation.new(:'-', right)
990
+ else
991
+ Arel::Nodes::Subtraction.new(left, right)
992
+ end
829
993
  when '*'
830
994
  Arel::Nodes::Multiplication.new(left, right)
831
995
  when '/'
@@ -849,9 +1013,17 @@ module Arel
849
1013
  when '|'
850
1014
  Arel::Nodes::BitwiseOr.new(left, right)
851
1015
  when '#'
852
- Arel::Nodes::BitwiseXor.new(left, right)
1016
+ if left.nil?
1017
+ Arel::Nodes::UnaryOperation.new(:'#', right)
1018
+ else
1019
+ Arel::Nodes::BitwiseXor.new(left, right)
1020
+ end
853
1021
  when '~'
854
- Arel::Nodes::BitwiseNot.new(right)
1022
+ if left.nil?
1023
+ Arel::Nodes::BitwiseNot.new(right)
1024
+ else
1025
+ Arel::Nodes::Regexp.new(left, right, true)
1026
+ end
855
1027
  when '<<'
856
1028
  Arel::Nodes::BitwiseShiftLeft.new(left, right)
857
1029
  when '>>'
@@ -881,11 +1053,59 @@ module Arel
881
1053
  when '||'
882
1054
  Arel::Nodes::Concat.new(left, right)
883
1055
 
1056
+ # https://www.postgresql.org/docs/9.3/functions-net.html
1057
+ when '<<='
1058
+ Arel::Nodes::ContainedWithinEquals.new(left, right)
1059
+ when '>>='
1060
+ Arel::Nodes::ContainsEquals.new(left, right)
1061
+
1062
+ # https://www.postgresql.org/docs/9.4/functions-json.html
1063
+ when '->'
1064
+ Arel::Nodes::JsonGetObject.new(left, right)
1065
+ when '->>'
1066
+ Arel::Nodes::JsonGetField.new(left, right)
1067
+ when '#>'
1068
+ Arel::Nodes::JsonPathGetObject.new(left, right)
1069
+ when '#>>'
1070
+ Arel::Nodes::JsonPathGetField.new(left, right)
1071
+
1072
+ # https://www.postgresql.org/docs/9.4/functions-json.html#FUNCTIONS-JSONB-OP-TABLE
1073
+ when '?'
1074
+ Arel::Nodes::JsonbKeyExists.new(left, right)
1075
+ when '?|'
1076
+ if left.nil?
1077
+ Arel::Nodes::UnaryOperation.new(:'?|', right)
1078
+ else
1079
+ Arel::Nodes::JsonbAnyKeyExists.new(left, right)
1080
+ end
1081
+ when '?&'
1082
+ Arel::Nodes::JsonbAllKeyExists.new(left, right)
1083
+
1084
+ # https://www.postgresql.org/docs/9.3/functions-matching.html#FUNCTIONS-POSIX-TABLE
1085
+ when '~*'
1086
+ Arel::Nodes::Regexp.new(left, right, false)
1087
+ when '!~'
1088
+ Arel::Nodes::NotRegexp.new(left, right, true)
1089
+ when '!~*'
1090
+ Arel::Nodes::NotRegexp.new(left, right, false)
1091
+
884
1092
  else
885
- raise "Unknown operator `#{operator}`"
1093
+ if left.nil?
1094
+ Arel::Nodes::UnaryOperation.new(operator, right)
1095
+ else
1096
+ Arel::Nodes::InfixOperation.new(operator, left, right)
1097
+ end
886
1098
  end
887
1099
  end
888
1100
 
1101
+ def visit_DeallocateStmt(name: nil)
1102
+ Arel::Nodes::Dealocate.new name
1103
+ end
1104
+
1105
+ def visit_PrepareStmt(name:, argtypes: nil, query:)
1106
+ Arel::Nodes::Prepare.new name, argtypes && visit(argtypes), visit(query)
1107
+ end
1108
+
889
1109
  def visit(attribute, context = nil)
890
1110
  return attribute.map { |attr| visit(attr, context) } if attribute.is_a? Array
891
1111
 
@@ -972,27 +1192,55 @@ module Arel
972
1192
  generate_operator(testexpr, Arel::Nodes::All.new(subselect), operator)
973
1193
 
974
1194
  when PgQuery::SUBLINK_TYPE_ANY
975
- generate_operator(testexpr, Arel::Nodes::Any.new(subselect), operator)
1195
+ if operator.nil?
1196
+ Arel::Nodes::In.new(testexpr, subselect)
1197
+ else
1198
+ generate_operator(testexpr, Arel::Nodes::Any.new(subselect), operator)
1199
+ end
976
1200
 
977
1201
  when PgQuery::SUBLINK_TYPE_ROWCOMPARE
978
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/42'
1202
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/42'
979
1203
 
980
1204
  when PgQuery::SUBLINK_TYPE_EXPR
981
1205
  Arel::Nodes::Grouping.new(subselect)
982
1206
 
983
1207
  when PgQuery::SUBLINK_TYPE_MULTIEXPR
984
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/43'
1208
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/43'
985
1209
 
986
1210
  when PgQuery::SUBLINK_TYPE_ARRAY
987
1211
  Arel::Nodes::ArraySubselect.new(subselect)
988
1212
 
989
1213
  when PgQuery::SUBLINK_TYPE_CTE
990
- raise 'https://github.com/mvgijssel/arel_toolkit/issues/44'
1214
+ boom 'https://github.com/mvgijssel/arel_toolkit/issues/44'
991
1215
 
992
1216
  else
993
- raise "Unknown sublinktype: #{type}"
1217
+ boom "Unknown sublinktype: #{type}"
994
1218
  end
995
1219
  end
1220
+
1221
+ def maybe_add_grouping(node)
1222
+ case node
1223
+ when Arel::Nodes::Binary
1224
+ Arel::Nodes::Grouping.new(node)
1225
+ else
1226
+ node
1227
+ end
1228
+ end
1229
+
1230
+ def boom(message, backtrace = nil)
1231
+ new_message = <<~STRING
1232
+
1233
+
1234
+ SQL: #{sql}
1235
+ BINDS: #{binds}
1236
+ message: #{message}
1237
+
1238
+ STRING
1239
+
1240
+ raise(Arel::SqlToArel::Error, new_message, backtrace) if backtrace
1241
+
1242
+ raise Arel::SqlToArel::Error, new_message
1243
+ end
996
1244
  end
997
1245
  end
998
1246
  end