sunstone 7.0.0 → 7.2.0

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.
@@ -0,0 +1,19 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module Sunstone
4
+ module Quoting
5
+ extend ActiveSupport::Concern
6
+
7
+ module ClassMethods # :nodoc:
8
+
9
+ # Quotes column names for use in SQL queries.
10
+ def quote_column_name(name) # :nodoc:
11
+ name
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ end
@@ -27,8 +27,7 @@ module ActiveRecord
27
27
  return @definitions[table_name]
28
28
  end
29
29
 
30
- response = @connection.get("/#{table_name}/schema")
31
-
30
+ response = with_raw_connection { |conn| conn.get("/#{table_name}/schema") }
32
31
  @definitions[table_name] = JSON.parse(response.body)
33
32
  rescue ::Sunstone::Exception::NotFound
34
33
  raise ActiveRecord::StatementInvalid, "Table \"#{table_name}\" does not exist"
@@ -40,7 +39,9 @@ module ActiveRecord
40
39
  # - format_type includes the column size constraint, e.g. varchar(50)
41
40
  # - ::regclass is a function that gives the id for a table name
42
41
  def column_definitions(table_name) # :nodoc:
43
- definition(table_name)['columns']
42
+ # TODO: settle on schema, I think we've switched to attributes, so
43
+ # columns can be removed soon?
44
+ definition(table_name)['attributes'] || definition(table_name)['columns']
44
45
  end
45
46
 
46
47
  # Returns the limit definition of the table (the maximum limit that can
@@ -50,7 +51,7 @@ module ActiveRecord
50
51
  end
51
52
 
52
53
  def tables
53
- JSON.parse(@connection.get('/tables').body)
54
+ JSON.parse(with_raw_connection { |conn| conn.get('/tables').body })
54
55
  end
55
56
 
56
57
  def views
@@ -63,7 +64,7 @@ module ActiveRecord
63
64
  end
64
65
 
65
66
  def lookup_cast_type(options)
66
- type_map.lookup(options['type'], options.symbolize_keys)
67
+ @type_map.lookup(options['type'], options.symbolize_keys)
67
68
  end
68
69
 
69
70
  def fetch_type_metadata(options)
@@ -13,11 +13,11 @@ module ActiveRecord
13
13
  #
14
14
  # +value+ The raw input, as provided from the database.
15
15
  def deserialize(value)
16
- value.nil? ? nil : Base64.strict_decode64(value)
16
+ value.nil? ? nil : Base64.strict_decode64(value)
17
17
  end
18
18
 
19
- # Casts a value from the ruby type to a type that the database knows how
20
- # to understand. The returned value from this method should be a
19
+ # Casts a value from the ruby type to a type that the database knows
20
+ # how to understand. The returned value from this method should be a
21
21
  # +String+, +Numeric+, +Date+, +Time+, +Symbol+, +true+, +false+, or
22
22
  # +nil+.
23
23
  def serialize(value)
@@ -3,6 +3,7 @@ require 'active_record/connection_adapters/abstract_adapter'
3
3
  require 'arel/nodes/relation'
4
4
  require 'arel/visitors/to_sql_extensions'
5
5
 
6
+ require 'active_record/connection_adapters/sunstone/quoting'
6
7
  require 'active_record/connection_adapters/sunstone/database_statements'
7
8
  require 'active_record/connection_adapters/sunstone/schema_statements'
8
9
  require 'active_record/connection_adapters/sunstone/schema_dumper'
@@ -15,32 +16,6 @@ require 'active_record/connection_adapters/sunstone/type/uuid'
15
16
  require 'active_record/connection_adapters/sunstone/type/json'
16
17
 
17
18
  module ActiveRecord
18
- module ConnectionHandling # :nodoc:
19
-
20
- VALID_SUNSTONE_CONN_PARAMS = [:url, :host, :port, :api_key, :use_ssl, :user_agent, :ca_cert]
21
-
22
- # Establishes a connection to the database that's used by all Active Record
23
- # objects
24
- def sunstone_connection(config)
25
- conn_params = config.symbolize_keys
26
- conn_params.delete_if { |_, v| v.nil? }
27
-
28
- if conn_params[:url]
29
- uri = URI.parse(conn_params.delete(:url))
30
- conn_params[:api_key] ||= (uri.user ? CGI.unescape(uri.user) : nil)
31
- conn_params[:host] ||= uri.host
32
- conn_params[:port] ||= uri.port
33
- conn_params[:use_ssl] ||= (uri.scheme == 'https')
34
- end
35
-
36
- # Forward only valid config params to Sunstone::Connection
37
- conn_params.slice!(*VALID_SUNSTONE_CONN_PARAMS)
38
-
39
- client = ::Sunstone::Connection.new(conn_params)
40
- ConnectionAdapters::SunstoneAPIAdapter.new(client, logger, conn_params, config)
41
- end
42
- end
43
-
44
19
  module ConnectionAdapters
45
20
  # The SunstoneAPI adapter.
46
21
  #
@@ -54,6 +29,7 @@ module ActiveRecord
54
29
  # <encoding></tt> call on the connection.
55
30
  class SunstoneAPIAdapter < AbstractAdapter
56
31
  ADAPTER_NAME = 'Sunstone'.freeze
32
+ VALID_SUNSTONE_CONN_PARAMS = [:url, :host, :port, :api_key, :use_ssl, :user_agent, :ca_cert]
57
33
 
58
34
  NATIVE_DATABASE_TYPES = {
59
35
  string: { name: "string" },
@@ -61,8 +37,14 @@ module ActiveRecord
61
37
  json: { name: "json" },
62
38
  boolean: { name: "boolean" }
63
39
  }
40
+
41
+ class << self
42
+ def new_client(conn_params)
43
+ ::Sunstone::Connection.new(conn_params)
44
+ end
45
+ end
64
46
 
65
- # include PostgreSQL::Quoting
47
+ include Sunstone::Quoting
66
48
  # include PostgreSQL::ReferentialIntegrity
67
49
  include Sunstone::SchemaStatements
68
50
  include Sunstone::DatabaseStatements
@@ -72,35 +54,61 @@ module ActiveRecord
72
54
  def supports_statement_cache?
73
55
  false
74
56
  end
57
+
58
+ def default_prepared_statements
59
+ false
60
+ end
75
61
 
76
- def clear_cache!
62
+ def clear_cache!(new_connection: false)
77
63
  # TODO move @definitions to using @schema_cache
78
64
  @definitions = {}
79
65
  end
80
66
 
81
67
  # Initializes and connects a SunstoneAPI adapter.
82
- def initialize(connection, logger, connection_parameters, config)
83
- super(connection, logger, config.reverse_merge(prepared_statements: false))
68
+ def initialize(...)
69
+ super
84
70
 
85
- @prepared_statement_status = Concurrent::ThreadLocalVar.new(false)
86
- @connection_parameters = connection_parameters
71
+ conn_params = @config.compact
72
+ if conn_params[:url]
73
+ uri = URI.parse(conn_params.delete(:url))
74
+ conn_params[:api_key] ||= (uri.user ? CGI.unescape(uri.user) : nil)
75
+ conn_params[:host] ||= uri.host
76
+ conn_params[:port] ||= uri.port
77
+ conn_params[:use_ssl] ||= (uri.scheme == 'https')
78
+ end
87
79
 
88
- @type_map = Type::HashLookupTypeMap.new
89
- initialize_type_map(@type_map)
80
+ # Forward only valid config params to Sunstone::Connection
81
+ conn_params.slice!(*VALID_SUNSTONE_CONN_PARAMS)
82
+
83
+ @connection_parameters = conn_params
84
+
85
+ @max_identifier_length = nil
86
+ @type_map = nil
87
+ @raw_connection = nil
90
88
  end
91
89
 
92
- def active?
93
- @connection.active?
90
+ def url(path=nil)
91
+ "http#{@connection_parameters[:use_ssl] ? 's' : ''}://#{@connection_parameters[:host]}#{@connection_parameters[:port] != 80 ? (@connection_parameters[:port] == 443 && @connection_parameters[:use_ssl] ? '' : ":#{@connection_parameters[:port]}") : ''}#{path}"
94
92
  end
95
93
 
96
- def reconnect!
94
+ def active?
95
+ @raw_connection&.active?
96
+ end
97
+
98
+ def reconnect
97
99
  super
98
- @connection.reconnect!
100
+ @raw_connection&.reconnect!
99
101
  end
100
102
 
101
103
  def disconnect!
102
104
  super
103
- @connection.disconnect!
105
+ @raw_connection&.disconnect!
106
+ @raw_connection = nil
107
+ end
108
+
109
+ def discard! # :nodoc:
110
+ super
111
+ @raw_connection = nil
104
112
  end
105
113
 
106
114
  # Executes the delete statement and returns the number of rows affected.
@@ -134,11 +142,18 @@ module ActiveRecord
134
142
  end
135
143
 
136
144
  def server_config
137
- JSON.parse(@connection.get("/configuration").body)
145
+ with_raw_connection do |conn|
146
+ JSON.parse(conn.get("/configuration").body)
147
+ end
148
+ end
149
+
150
+ def return_value_after_insert?(column) # :nodoc:
151
+ column.auto_populated?
138
152
  end
139
153
 
140
154
  def lookup_cast_type_from_column(column) # :nodoc:
141
- cast_type = type_map.lookup(column.sql_type, {
155
+ verify! if type_map.nil?
156
+ cast_type = @type_map.lookup(column.sql_type, {
142
157
  limit: column.limit,
143
158
  precision: column.precision,
144
159
  scale: column.scale
@@ -167,55 +182,67 @@ module ActiveRecord
167
182
  true
168
183
  end
169
184
 
170
- # Executes an INSERT query and returns the new record's ID
171
- #
172
- # +id_value+ will be returned unless the value is nil, in
173
- # which case the database will attempt to calculate the last inserted
174
- # id and return that value.
175
- #
176
- # If the next id was calculated in advance (as in Oracle), it should be
177
- # passed in as +id_value+.
178
- def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
179
- exec_insert(arel, name, binds, pk, sequence_name)
185
+ # Executes an INSERT query and returns a hash of the object and
186
+ # any updated relations. This is different from AR which returns an ID
187
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [], returning: nil)
188
+ exec_insert(arel, name, binds, pk, sequence_name, returning: returning)
180
189
  end
181
190
  alias create insert
182
191
 
192
+ # Connects to a StandardAPI server and sets up the adapter depending
193
+ # on the connected server's characteristics.
194
+ def connect
195
+ @raw_connection = self.class.new_client(@connection_parameters)
196
+ end
197
+
198
+ def reconnect
199
+ @raw_connection&.reconnect!
200
+ connect unless @raw_connection
201
+ end
202
+
203
+ # Configures the encoding, verbosity, schema search path, and time zone of the connection.
204
+ # This is called by #connect and should not be called manually.
205
+ def configure_connection
206
+ super
207
+
208
+ reload_type_map
209
+ end
210
+
183
211
  def reload_type_map
184
- type_map.clear
212
+ if @type_map
213
+ type_map.clear
214
+ else
215
+ @type_map = Type::HashLookupTypeMap.new
216
+ end
217
+
185
218
  initialize_type_map
186
219
  end
187
220
 
188
221
  private
189
222
 
190
- def type_map
191
- @type_map ||= Type::HashLookupTypeMap.new.tap do |mapping|
192
- initialize_type_map(mapping)
193
- end
194
- end
195
-
196
- def initialize_type_map(m = type_map)
197
- self.class.initialize_type_map(m)
198
- load_additional_types
223
+ attr_reader :type_map
224
+
225
+ def initialize_type_map(m = nil)
226
+ self.class.initialize_type_map(m || @type_map)
199
227
  end
200
228
 
201
- def initialize_type_map(m) # :nodoc:
202
- m.register_type 'boolean', Type::Boolean.new
203
- m.register_type 'string' do |_, options|
204
- Type::String.new(**options.slice(:limit))
205
- end
206
- m.register_type 'integer' do |_, options|
207
- Type::Integer.new(**options.slice(:limit))
229
+ def self.initialize_type_map(m) # :nodoc:
230
+ m.register_type 'boolean', Type::Boolean.new
231
+ m.register_type 'binary' do |_, options|
232
+ Sunstone::Type::Binary.new(**options.slice(:limit))
208
233
  end
209
- m.register_type 'decimal' do |_, options|
234
+ m.register_type 'datetime', Sunstone::Type::DateTime.new
235
+ m.register_type 'decimal' do |_, options|
210
236
  Type::Decimal.new(**options.slice(:precision, :scale))
211
237
  end
212
- m.register_type 'binary' do |_, options|
213
- Sunstone::Type::Binary.new(**options.slice(:limit))
238
+ m.register_type 'integer' do |_, options|
239
+ Type::Integer.new(**options.slice(:limit))
214
240
  end
215
-
216
- m.register_type 'datetime', Sunstone::Type::DateTime.new
217
- m.register_type 'json', Sunstone::Type::Json.new
218
- m.register_type 'uuid', Sunstone::Type::Uuid.new
241
+ m.register_type 'json', Sunstone::Type::Json.new
242
+ m.register_type 'string' do |_, options|
243
+ Type::String.new(**options.slice(:limit))
244
+ end
245
+ m.register_type 'uuid', Sunstone::Type::Uuid.new
219
246
 
220
247
  if defined?(Sunstone::Type::EWKB)
221
248
  m.register_type 'ewkb', Sunstone::Type::EWKB.new
@@ -3,24 +3,24 @@ require 'arel/visitors/visitor'
3
3
  module Arel
4
4
  module Visitors
5
5
  class Sunstone < Arel::Visitors::Visitor
6
-
6
+
7
7
  def compile(node, collector = Arel::Collectors::Sunstone.new)
8
8
  accept(node, collector).value
9
9
  end
10
-
10
+
11
11
  private
12
-
12
+
13
13
  def visit_Arel_Nodes_SelectStatement o, collector
14
14
  collector.table = o.cores.first.source.left.name
15
15
 
16
16
  collector = o.cores.inject(collector) { |c,x|
17
17
  visit_Arel_Nodes_SelectCore(x, c)
18
18
  }
19
-
19
+
20
20
  if !o.orders.empty?
21
21
  collector.order = o.orders.map { |x| visit(x, collector) }
22
22
  end
23
-
23
+
24
24
  collector = maybe_visit o.limit, collector
25
25
  collector = maybe_visit o.offset, collector
26
26
  collector = maybe_visit o.eager_load, collector
@@ -61,27 +61,36 @@ module Arel
61
61
  collector.request_type = Net::HTTP::Post
62
62
  collector.table = o.relation.name
63
63
  collector.operation = :insert
64
-
64
+
65
65
  if o.values
66
66
  if o.values.is_a?(Arel::Nodes::SqlLiteral) && o.values == 'DEFAULT VALUES'
67
67
  collector.updates = {}
68
68
  else
69
- collector.updates = {}
70
-
71
- o.columns.map(&:name).zip(o.values.expr.first).to_h.each do |k, v|
72
- collector.updates[k] = case v
73
- when Nodes::SqlLiteral, Nodes::BindParam, ActiveModel::Attribute
74
- visit(v, collector)
75
- else
76
- v
77
- end
78
- end
69
+ values = visit(o.values, collector)
70
+ collector.updates = o.columns.map(&:name).zip(values).to_h
79
71
  end
80
72
  end
81
-
73
+
82
74
  collector
83
75
  end
84
-
76
+
77
+ def visit_Arel_Nodes_ValuesList o, collector
78
+ o.rows[0].map do |v|
79
+ visit_subrelation v, collector
80
+ end
81
+ end
82
+
83
+ def visit_subrelation v, collector
84
+ case v
85
+ when Array
86
+ v.map { |v2| visit_subrelation v2, collector }
87
+ when Hash
88
+ v.transform_values { |v2| visit_subrelation v2, collector }
89
+ else
90
+ visit(v, collector)
91
+ end
92
+ end
93
+
85
94
  def find_bottom(hash)
86
95
  if hash.is_a?(Hash)
87
96
  if hash.values.first.is_a?(Array) || hash.values.first.is_a?(Hash)
@@ -98,10 +107,10 @@ module Arel
98
107
  end
99
108
  end
100
109
  end
101
-
110
+
102
111
  def add_to_bottom_of_hash_or_array(hash, value)
103
112
  hash = find_bottom(hash)
104
-
113
+
105
114
  if hash.is_a?(Hash)
106
115
  nkey = hash.keys.first
107
116
  nvalue = hash.values.first
@@ -147,26 +156,26 @@ module Arel
147
156
  #
148
157
  def visit_Arel_Nodes_UpdateStatement o, collector
149
158
  collector.request_type = Net::HTTP::Patch
150
-
159
+
151
160
  collector.table = o.relation.name
152
161
  collector.operation = :update
153
-
162
+
154
163
  # collector.id = o.wheres.first.children.first.right
155
164
  if !o.wheres.empty?
156
165
  collector.where = o.wheres.map { |x| visit(x, collector) }.inject([]) { |c, w|
157
166
  w.is_a?(Array) ? c += w : c << w
158
167
  }
159
168
  end
160
-
169
+
161
170
  if collector.where.size != 1 && collector.where.first.size != 1 && !collector.where.first['id']
162
171
  raise 'Upsupported'
163
172
  end
164
-
173
+
165
174
  collector.where = collector.where.first
166
-
175
+
167
176
  if o.values
168
177
  collector.updates = {}
169
-
178
+
170
179
  o.values.map { |x| visit(x, collector) }.each do |value|
171
180
  value.each do |key, v|
172
181
  if key.is_a?(Hash)
@@ -191,7 +200,7 @@ module Arel
191
200
  end
192
201
  end
193
202
  end
194
-
203
+
195
204
  collector
196
205
  end
197
206
  #
@@ -426,7 +435,7 @@ module Arel
426
435
  def visit_Arel_Nodes_Descending o, collector
427
436
  { visit(o.expr, collector) => :desc }
428
437
  end
429
-
438
+
430
439
  def visit_Arel_Nodes_RandomOrdering o, collector
431
440
  :random
432
441
  end
@@ -468,7 +477,7 @@ module Arel
468
477
  #
469
478
  def visit_Arel_Nodes_Count o, collector
470
479
  collector.operation = :calculate
471
-
480
+
472
481
  collector.columns ||= []
473
482
  collector.columns << {:count => (o.expressions.first.is_a?(Arel::Attributes::Attribute) ? o.expressions.first.name : o.expressions.first) }
474
483
  # collector.columns = visit o.expressions.first, collector
@@ -476,7 +485,7 @@ module Arel
476
485
 
477
486
  def visit_Arel_Nodes_Sum o, collector
478
487
  collector.operation = :calculate
479
-
488
+
480
489
  collector.columns ||= []
481
490
  collector.columns << {:sum => (o.expressions.first.is_a?(Arel::Attributes::Attribute) ? o.expressions.first.name : o.expressions.first) }
482
491
  # collector.columns = visit o.expressions.first, collector
@@ -484,7 +493,7 @@ module Arel
484
493
 
485
494
  def visit_Arel_Nodes_Max o, collector
486
495
  collector.operation = :calculate
487
-
496
+
488
497
  collector.columns ||= []
489
498
  if o.expressions.first.is_a?(Arel::Attributes::Attribute)
490
499
  relation = o.expressions.first.relation
@@ -686,11 +695,11 @@ module Arel
686
695
  end
687
696
  end
688
697
  alias_method :visit_Arel_Nodes_HomogeneousIn, :visit_Arel_Nodes_In
689
-
698
+
690
699
  def visit_Arel_Nodes_NotIn o, collector
691
700
  key = visit(o.left, collector)
692
701
  value = {not_in: visit(o.right, collector)}
693
-
702
+
694
703
  if hash.is_a?(Hash)
695
704
  add_to_bottom_of_hash_or_array(key, value)
696
705
  key
@@ -698,10 +707,10 @@ module Arel
698
707
  {key => value}
699
708
  end
700
709
  end
701
-
710
+
702
711
  # You merge a into b if a keys do not colid with b keys
703
712
  def mergeable?(hash_a, hash_b)
704
-
713
+
705
714
  hash_a.each do |key, value_a|
706
715
  #TODO: one day maybe just use symbols for all keys?
707
716
  if hash_b.has_key?(key.to_sym) || hash_b.has_key?(key.to_s)
@@ -715,7 +724,7 @@ module Arel
715
724
  end
716
725
  true
717
726
  end
718
-
727
+
719
728
  def visit_Arel_Nodes_And o, collector
720
729
  ors = []
721
730
 
@@ -730,16 +739,16 @@ module Arel
730
739
  ors << value
731
740
  end
732
741
  end
733
-
742
+
734
743
  result = []
735
744
  ors.each_with_index do |c, i|
736
745
  result << c
737
746
  result << 'AND' if ors.size != i + 1
738
747
  end
739
-
748
+
740
749
  result.size == 1 ? result.first : result
741
750
  end
742
-
751
+
743
752
  def visit_Arel_Nodes_Or o, collector
744
753
  [visit(o.left, collector), 'OR', visit(o.right, collector)]
745
754
  end
@@ -748,10 +757,12 @@ module Arel
748
757
  case o.left
749
758
  when Arel::Nodes::UnqualifiedColumn
750
759
  case o.right
751
- when Arel::Attributes::Attribute, Arel::Nodes::BindParam, ActiveModel::Attribute
752
- { visit(o.left.expr, collector) => visit(o.right, collector) }
760
+ when Array
761
+ { visit(o.left.expr, collector) => o.right.map { |i| i.transform_values { |v| visit(v, collector) } } }
762
+ when Hash
763
+ { visit(o.left.expr, collector) => o.right.transform_values { |v| visit(v, collector) } }
753
764
  else
754
- { visit(o.left.expr, collector) => o.right }
765
+ { visit(o.left.expr, collector) => visit(o.right, collector) }
755
766
  end
756
767
  when Arel::Attributes::Attribute, Arel::Nodes::BindParam, ActiveModel::Attribute
757
768
  { visit(o.left, collector) => visit(o.right, collector) }
@@ -770,7 +781,7 @@ module Arel
770
781
  okey.merge!(value)
771
782
  hash
772
783
  end
773
-
784
+
774
785
  def add_to_bottom_of_hash(hash, value)
775
786
  okey = hash
776
787
  while okey.is_a?(Hash) && (okey.values.first.is_a?(Hash) || okey.values.first.is_a?(Array))
@@ -785,11 +796,11 @@ module Arel
785
796
  okey[nkey] = { nvalue => value }
786
797
  hash
787
798
  end
788
-
799
+
789
800
  def visit_Arel_Nodes_Equality o, collector
790
801
  key = visit(o.left, collector)
791
802
  value = (o.right.nil? ? nil : visit(o.right, collector))
792
-
803
+
793
804
  if key.is_a?(Hash)
794
805
  add_to_bottom_of_hash(key, {eq: value})
795
806
  elsif o.left.class.name == 'Arel::Attributes::Key'
@@ -798,7 +809,7 @@ module Arel
798
809
  { key => value }
799
810
  end
800
811
  end
801
-
812
+
802
813
  def visit_Arel_Nodes_TSMatch(o, collector)
803
814
  key = visit(o.left, collector)
804
815
  value = { ts_match: (o.right.nil? ? nil : visit(o.right, collector)) }
@@ -814,11 +825,11 @@ module Arel
814
825
  hash
815
826
  end
816
827
  end
817
-
828
+
818
829
  def visit_Arel_Nodes_TSVector(o, collector)
819
830
  visit(o.attribute, collector)
820
831
  end
821
-
832
+
822
833
  def visit_Arel_Nodes_TSQuery(o, collector)
823
834
  if o.language
824
835
  [visit(o.expression, collector), visit(o.language, collector)]
@@ -826,11 +837,11 @@ module Arel
826
837
  visit(o.expression, collector)
827
838
  end
828
839
  end
829
-
840
+
830
841
  def visit_Arel_Nodes_HasKey o, collector
831
842
  key = visit(o.left, collector)
832
843
  value = {has_key: (o.right.nil? ? nil : o.right.to_s)}
833
-
844
+
834
845
  if key.is_a?(Hash)
835
846
  okey = key
836
847
  while okey.values.first.is_a?(Hash)
@@ -847,7 +858,7 @@ module Arel
847
858
  def visit_Arel_Nodes_HasKeys o, collector
848
859
  key = visit(o.left, collector)
849
860
  value = { has_keys: visit(o.right, collector) }
850
-
861
+
851
862
  if key.is_a?(Hash)
852
863
  okey = key
853
864
  while okey.values.first.is_a?(Hash)
@@ -864,7 +875,7 @@ module Arel
864
875
  def visit_Arel_Nodes_HasAnyKey o, collector
865
876
  key = visit(o.left, collector)
866
877
  value = { has_any_key: visit(o.right, collector) }
867
-
878
+
868
879
  if key.is_a?(Hash)
869
880
  okey = key
870
881
  while okey.values.first.is_a?(Hash)
@@ -894,11 +905,11 @@ module Arel
894
905
  def visit_Arel_Nodes_UnqualifiedColumn o, collector
895
906
  o.name
896
907
  end
897
-
908
+
898
909
  def visit_Arel_Attributes_Cast(o, collector)
899
910
  visit(o.relation, collector) # No casting yet
900
911
  end
901
-
912
+
902
913
  def visit_Arel_Attributes_Key o, collector
903
914
  "#{visit(o.relation, collector)}.#{o.name}"
904
915
  end
@@ -938,7 +949,7 @@ module Arel
938
949
  # def visit_Arel_Attributes_EmptyRelation o, collector, top=true
939
950
  # o.for_write ? "#{o.name}_attributes" : o.name
940
951
  # end
941
-
952
+
942
953
  def visit_Arel_Attributes_Attribute o, collector
943
954
  join_name = o.relation.table_alias || o.relation.name
944
955
 
@@ -963,7 +974,7 @@ module Arel
963
974
  o.value_for_database
964
975
  end
965
976
  end
966
-
977
+
967
978
  def visit_Arel_Nodes_BindParam o, collector
968
979
  a = collector.add_bind(o.value)
969
980
  o.is_a?(Arel::Nodes::BindParam) ? o : a
@@ -1060,11 +1071,11 @@ module Arel
1060
1071
  collector
1061
1072
  end
1062
1073
  end
1063
-
1074
+
1064
1075
  def maybe_visit thing, collector
1065
1076
  return collector unless thing
1066
1077
  visit thing, collector
1067
1078
  end
1068
1079
  end
1069
1080
  end
1070
- end
1081
+ end