activerecord-sqlserver-adapter 6.0.3 → 6.1.0.0.rc1

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +1 -3
  3. data/CHANGELOG.md +18 -77
  4. data/Gemfile +1 -2
  5. data/README.md +13 -2
  6. data/VERSION +1 -1
  7. data/activerecord-sqlserver-adapter.gemspec +1 -1
  8. data/appveyor.yml +7 -5
  9. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +0 -2
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +0 -13
  11. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +7 -4
  12. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +0 -2
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +0 -2
  14. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
  15. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +27 -15
  16. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +3 -3
  17. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -1
  18. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -3
  19. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +7 -5
  20. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +27 -7
  21. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +0 -1
  22. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +2 -2
  23. data/lib/active_record/connection_adapters/sqlserver/utils.rb +1 -1
  24. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +83 -66
  25. data/lib/active_record/connection_adapters/sqlserver_column.rb +17 -0
  26. data/lib/active_record/sqlserver_base.rb +9 -15
  27. data/lib/active_record/tasks/sqlserver_database_tasks.rb +17 -14
  28. data/lib/arel/visitors/sqlserver.rb +60 -28
  29. data/test/cases/adapter_test_sqlserver.rb +17 -15
  30. data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
  31. data/test/cases/coerced_tests.rb +470 -95
  32. data/test/cases/disconnected_test_sqlserver.rb +39 -0
  33. data/test/cases/execute_procedure_test_sqlserver.rb +9 -0
  34. data/test/cases/in_clause_test_sqlserver.rb +27 -0
  35. data/test/cases/migration_test_sqlserver.rb +7 -0
  36. data/test/cases/order_test_sqlserver.rb +7 -0
  37. data/test/cases/primary_keys_test_sqlserver.rb +103 -0
  38. data/test/cases/rake_test_sqlserver.rb +3 -2
  39. data/test/cases/schema_dumper_test_sqlserver.rb +9 -0
  40. data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
  41. data/test/models/sqlserver/sst_string_collation.rb +3 -0
  42. data/test/schema/sqlserver_specific_schema.rb +7 -0
  43. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
  44. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
  45. data/test/support/sql_counter_sqlserver.rb +14 -12
  46. metadata +23 -9
@@ -92,7 +92,7 @@ module ActiveRecord
92
92
  @schema = @parts.first
93
93
  end
94
94
  rest = scanner.rest
95
- rest = rest.starts_with?(".") ? rest[1..-1] : rest[0..-1]
95
+ rest = rest.start_with?(".") ? rest[1..-1] : rest[0..-1]
96
96
  @object = unquote(rest)
97
97
  @parts << @object
98
98
  end
@@ -59,13 +59,73 @@ module ActiveRecord
59
59
  self.use_output_inserted = true
60
60
  self.exclude_output_inserted_table_names = Concurrent::Map.new { false }
61
61
 
62
- def initialize(connection, logger = nil, config = {})
62
+ class << self
63
+ def new_client(config)
64
+ case config[:mode]
65
+ when :dblib
66
+ require "tiny_tds"
67
+ dblib_connect(config)
68
+ else
69
+ raise ArgumentError, "Unknown connection mode in #{config.inspect}."
70
+ end
71
+ end
72
+
73
+ def dblib_connect(config)
74
+ TinyTds::Client.new(
75
+ dataserver: config[:dataserver],
76
+ host: config[:host],
77
+ port: config[:port],
78
+ username: config[:username],
79
+ password: config[:password],
80
+ database: config[:database],
81
+ tds_version: config[:tds_version] || "7.3",
82
+ appname: config_appname(config),
83
+ login_timeout: config_login_timeout(config),
84
+ timeout: config_timeout(config),
85
+ encoding: config_encoding(config),
86
+ azure: config[:azure],
87
+ contained: config[:contained]
88
+ ).tap do |client|
89
+ if config[:azure]
90
+ client.execute("SET ANSI_NULLS ON").do
91
+ client.execute("SET ANSI_NULL_DFLT_ON ON").do
92
+ client.execute("SET ANSI_PADDING ON").do
93
+ client.execute("SET ANSI_WARNINGS ON").do
94
+ else
95
+ client.execute("SET ANSI_DEFAULTS ON").do
96
+ end
97
+ client.execute("SET QUOTED_IDENTIFIER ON").do
98
+ client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do
99
+ client.execute("SET IMPLICIT_TRANSACTIONS OFF").do
100
+ client.execute("SET TEXTSIZE 2147483647").do
101
+ client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do
102
+ end
103
+ rescue TinyTds::Error => e
104
+ raise ActiveRecord::NoDatabaseError if e.message.match(/database .* does not exist/i)
105
+ raise e
106
+ end
107
+
108
+ def config_appname(config)
109
+ config[:appname] || configure_application_name || Rails.application.class.name.split("::").first rescue nil
110
+ end
111
+
112
+ def config_login_timeout(config)
113
+ config[:login_timeout].present? ? config[:login_timeout].to_i : nil
114
+ end
115
+
116
+ def config_timeout(config)
117
+ config[:timeout].present? ? config[:timeout].to_i / 1000 : nil
118
+ end
119
+
120
+ def config_encoding(config)
121
+ config[:encoding].present? ? config[:encoding] : nil
122
+ end
123
+ end
124
+
125
+ def initialize(connection, logger, _connection_options, config)
63
126
  super(connection, logger, config)
64
- # Our Responsibility
65
127
  @connection_options = config
66
- connect
67
- initialize_dateformatter
68
- use_database
128
+ configure_connection
69
129
  end
70
130
 
71
131
  # === Abstract Adapter ========================================== #
@@ -226,6 +286,14 @@ module ActiveRecord
226
286
  do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"
227
287
  end
228
288
 
289
+ def configure_connection
290
+ @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first
291
+ @version_year = version_year
292
+
293
+ initialize_dateformatter
294
+ use_database
295
+ end
296
+
229
297
  # === Abstract Adapter (Misc Support) =========================== #
230
298
 
231
299
  def tables_with_referential_integrity
@@ -375,6 +443,8 @@ module ActiveRecord
375
443
 
376
444
  def translate_exception(e, message:, sql:, binds:)
377
445
  case message
446
+ when /(SQL Server client is not connected)|(failed to execute statement)/i
447
+ ConnectionNotEstablished.new(message)
378
448
  when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
379
449
  RecordNotUnique.new(message, sql: sql, binds: binds)
380
450
  when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i
@@ -408,78 +478,18 @@ module ActiveRecord
408
478
 
409
479
  # === SQLServer Specific (Connection Management) ================ #
410
480
 
411
- def connect
412
- config = @connection_options
413
- @connection = case config[:mode]
414
- when :dblib
415
- dblib_connect(config)
416
- end
417
- @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first
418
- @version_year = version_year
419
- configure_connection
420
- end
421
-
422
481
  def connection_errors
423
482
  @connection_errors ||= [].tap do |errors|
424
483
  errors << TinyTds::Error if defined?(TinyTds::Error)
425
484
  end
426
485
  end
427
486
 
428
- def dblib_connect(config)
429
- TinyTds::Client.new(
430
- dataserver: config[:dataserver],
431
- host: config[:host],
432
- port: config[:port],
433
- username: config[:username],
434
- password: config[:password],
435
- database: config[:database],
436
- tds_version: config[:tds_version] || "7.3",
437
- appname: config_appname(config),
438
- login_timeout: config_login_timeout(config),
439
- timeout: config_timeout(config),
440
- encoding: config_encoding(config),
441
- azure: config[:azure],
442
- contained: config[:contained]
443
- ).tap do |client|
444
- if config[:azure]
445
- client.execute("SET ANSI_NULLS ON").do
446
- client.execute("SET ANSI_NULL_DFLT_ON ON").do
447
- client.execute("SET ANSI_PADDING ON").do
448
- client.execute("SET ANSI_WARNINGS ON").do
449
- else
450
- client.execute("SET ANSI_DEFAULTS ON").do
451
- end
452
- client.execute("SET QUOTED_IDENTIFIER ON").do
453
- client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do
454
- client.execute("SET IMPLICIT_TRANSACTIONS OFF").do
455
- client.execute("SET TEXTSIZE 2147483647").do
456
- client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do
457
- end
458
- end
459
-
460
- def config_appname(config)
461
- config[:appname] || configure_application_name || Rails.application.class.name.split("::").first rescue nil
462
- end
463
-
464
- def config_login_timeout(config)
465
- config[:login_timeout].present? ? config[:login_timeout].to_i : nil
466
- end
467
-
468
- def config_timeout(config)
469
- config[:timeout].present? ? config[:timeout].to_i / 1000 : nil
470
- end
471
-
472
- def config_encoding(config)
473
- config[:encoding].present? ? config[:encoding] : nil
474
- end
475
-
476
- def configure_connection; end
477
-
478
487
  def configure_application_name; end
479
488
 
480
489
  def initialize_dateformatter
481
490
  @database_dateformat = user_options_dateformat
482
491
  a, b, c = @database_dateformat.each_char.to_a
492
+
483
493
  [a, b, c].each { |f| f.upcase! if f == "y" }
484
494
  dateformat = "%#{a}-%#{b}-%#{c}"
485
495
  ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
@@ -502,6 +512,13 @@ module ActiveRecord
502
512
  def sqlserver_version
503
513
  @sqlserver_version ||= _raw_select("SELECT @@version", fetch: :rows).first.first.to_s
504
514
  end
515
+
516
+ private
517
+
518
+ def connect
519
+ @connection = self.class.new_client(@connection_options)
520
+ configure_connection
521
+ end
505
522
  end
506
523
  end
507
524
  end
@@ -27,6 +27,23 @@ module ActiveRecord
27
27
  def case_sensitive?
28
28
  collation && collation.match(/_CS/)
29
29
  end
30
+
31
+ private
32
+
33
+ # In the Rails version of this method there is an assumption that the `default` value will always be a
34
+ # `String` class, which must be true for the MySQL/PostgreSQL/SQLite adapters. However, in the SQL Server
35
+ # adapter the `default` value can also be Boolean/Date/Time/etc. Changed the implementation of this method
36
+ # to handle non-String `default` objects.
37
+ def deduplicated
38
+ @name = -name
39
+ @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata
40
+ @default = (default.is_a?(String) ? -default : default.dup.freeze) if default
41
+ @default_function = -default_function if default_function
42
+ @collation = -collation if collation
43
+ @comment = -comment if comment
44
+
45
+ freeze
46
+ end
30
47
  end
31
48
  end
32
49
  end
@@ -4,21 +4,15 @@ module ActiveRecord
4
4
  module ConnectionHandling
5
5
  def sqlserver_connection(config) #:nodoc:
6
6
  config = config.symbolize_keys
7
- config.reverse_merge! mode: :dblib
8
- mode = config[:mode].to_s.downcase.underscore.to_sym
9
- case mode
10
- when :dblib
11
- require "tiny_tds"
12
- else
13
- raise ArgumentError, "Unknown connection mode in #{config.inspect}."
14
- end
15
- ConnectionAdapters::SQLServerAdapter.new(nil, nil, config.merge(mode: mode))
16
- rescue TinyTds::Error => e
17
- if e.message.match(/database .* does not exist/i)
18
- raise ActiveRecord::NoDatabaseError
19
- else
20
- raise
21
- end
7
+ config.reverse_merge!(mode: :dblib)
8
+ config[:mode] = config[:mode].to_s.downcase.underscore.to_sym
9
+
10
+ ConnectionAdapters::SQLServerAdapter.new(
11
+ ConnectionAdapters::SQLServerAdapter.new_client(config),
12
+ logger,
13
+ nil,
14
+ config
15
+ )
22
16
  end
23
17
  end
24
18
  end
@@ -13,13 +13,18 @@ module ActiveRecord
13
13
  delegate :connection, :establish_connection, :clear_active_connections!,
14
14
  to: ActiveRecord::Base
15
15
 
16
+ def self.using_database_configurations?
17
+ true
18
+ end
19
+
16
20
  def initialize(configuration)
17
21
  @configuration = configuration
22
+ @configuration_hash = @configuration.configuration_hash
18
23
  end
19
24
 
20
25
  def create(master_established = false)
21
26
  establish_master_connection unless master_established
22
- connection.create_database configuration["database"], configuration.merge("collation" => default_collation)
27
+ connection.create_database configuration.database, configuration_hash.merge(collation: default_collation)
23
28
  establish_connection configuration
24
29
  rescue ActiveRecord::StatementInvalid => e
25
30
  if /database .* already exists/i === e.message
@@ -31,7 +36,7 @@ module ActiveRecord
31
36
 
32
37
  def drop
33
38
  establish_master_connection
34
- connection.drop_database configuration["database"]
39
+ connection.drop_database configuration.database
35
40
  end
36
41
 
37
42
  def charset
@@ -49,14 +54,14 @@ module ActiveRecord
49
54
  end
50
55
 
51
56
  def structure_dump(filename, extra_flags)
52
- server_arg = "-S #{Shellwords.escape(configuration['host'])}"
53
- server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration["port"]
57
+ server_arg = "-S #{Shellwords.escape(configuration_hash[:host])}"
58
+ server_arg += ":#{Shellwords.escape(configuration_hash[:port])}" if configuration_hash[:port]
54
59
  command = [
55
60
  "defncopy-ttds",
56
61
  server_arg,
57
- "-D #{Shellwords.escape(configuration['database'])}",
58
- "-U #{Shellwords.escape(configuration['username'])}",
59
- "-P #{Shellwords.escape(configuration['password'])}",
62
+ "-D #{Shellwords.escape(configuration_hash[:database])}",
63
+ "-U #{Shellwords.escape(configuration_hash[:username])}",
64
+ "-P #{Shellwords.escape(configuration_hash[:password])}",
60
65
  "-o #{Shellwords.escape(filename)}",
61
66
  ]
62
67
  table_args = connection.tables.map { |t| Shellwords.escape(t) }
@@ -80,16 +85,14 @@ module ActiveRecord
80
85
 
81
86
  private
82
87
 
83
- def configuration
84
- @configuration
85
- end
88
+ attr_reader :configuration, :configuration_hash
86
89
 
87
90
  def default_collation
88
- configuration["collation"] || DEFAULT_COLLATION
91
+ configuration_hash[:collation] || DEFAULT_COLLATION
89
92
  end
90
93
 
91
94
  def establish_master_connection
92
- establish_connection configuration.merge("database" => "master")
95
+ establish_connection configuration_hash.merge(database: "master")
93
96
  end
94
97
  end
95
98
 
@@ -110,9 +113,9 @@ module ActiveRecord
110
113
  end
111
114
 
112
115
  def configuration_host_ip(configuration)
113
- return nil unless configuration["host"]
116
+ return nil unless configuration.host
114
117
 
115
- Socket::getaddrinfo(configuration["host"], "echo", Socket::AF_INET)[0][3]
118
+ Socket::getaddrinfo(configuration.host, "echo", Socket::AF_INET)[0][3]
116
119
  end
117
120
 
118
121
  def local_ipaddr?(host_ip)
@@ -11,13 +11,14 @@ module Arel
11
11
 
12
12
  private
13
13
 
14
- # SQLServer ToSql/Visitor (Overides)
14
+ # SQLServer ToSql/Visitor (Overrides)
15
15
 
16
- def visit_Arel_Nodes_BindParam o, collector
17
- collector.add_bind(o.value) { |i| "@#{i - 1}" }
18
- end
16
+ BIND_BLOCK = proc { |i| "@#{i - 1}" }
17
+ private_constant :BIND_BLOCK
18
+
19
+ def bind_block; BIND_BLOCK; end
19
20
 
20
- def visit_Arel_Nodes_Bin o, collector
21
+ def visit_Arel_Nodes_Bin(o, collector)
21
22
  visit o.expr, collector
22
23
  collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} "
23
24
  end
@@ -28,26 +29,26 @@ module Arel
28
29
  visit o.right, collector
29
30
  end
30
31
 
31
- def visit_Arel_Nodes_UpdateStatement(o, a)
32
+ def visit_Arel_Nodes_UpdateStatement(o, collector)
32
33
  if o.orders.any? && o.limit.nil?
33
34
  o.limit = Nodes::Limit.new(9_223_372_036_854_775_807)
34
35
  end
35
36
  super
36
37
  end
37
38
 
38
- def visit_Arel_Nodes_Lock o, collector
39
+ def visit_Arel_Nodes_Lock(o, collector)
39
40
  o.expr = Arel.sql("WITH(UPDLOCK)") if o.expr.to_s =~ /FOR UPDATE/
40
41
  collector << " "
41
42
  visit o.expr, collector
42
43
  end
43
44
 
44
- def visit_Arel_Nodes_Offset o, collector
45
+ def visit_Arel_Nodes_Offset(o, collector)
45
46
  collector << OFFSET
46
47
  visit o.expr, collector
47
48
  collector << ROWS
48
49
  end
49
50
 
50
- def visit_Arel_Nodes_Limit o, collector
51
+ def visit_Arel_Nodes_Limit(o, collector)
51
52
  if node_value(o) == 0
52
53
  collector << FETCH0
53
54
  collector << ROWS_ONLY
@@ -63,7 +64,38 @@ module Arel
63
64
  super
64
65
  end
65
66
 
66
- def visit_Arel_Nodes_SelectStatement o, collector
67
+ def visit_Arel_Nodes_HomogeneousIn(o, collector)
68
+ collector.preparable = false
69
+
70
+ collector << quote_table_name(o.table_name) << "." << quote_column_name(o.column_name)
71
+
72
+ if o.type == :in
73
+ collector << " IN ("
74
+ else
75
+ collector << " NOT IN ("
76
+ end
77
+
78
+ values = o.casted_values
79
+
80
+ if values.empty?
81
+ collector << @connection.quote(nil)
82
+ elsif @connection.prepared_statements
83
+ # Monkey-patch start. Add query attribute bindings rather than just values.
84
+ column_name = o.column_name
85
+ column_type = o.attribute.relation.type_for_attribute(o.column_name)
86
+ attrs = values.map { |value| ActiveRecord::Relation::QueryAttribute.new(column_name, value, column_type) }
87
+
88
+ collector.add_binds(attrs, &bind_block)
89
+ # Monkey-patch end.
90
+ else
91
+ collector.add_binds(values, &bind_block)
92
+ end
93
+
94
+ collector << ")"
95
+ collector
96
+ end
97
+
98
+ def visit_Arel_Nodes_SelectStatement(o, collector)
67
99
  @select_statement = o
68
100
  distinct_One_As_One_Is_So_Not_Fetch o
69
101
  if o.with
@@ -90,7 +122,7 @@ module Arel
90
122
  collector << "OPTION (#{hints})"
91
123
  end
92
124
 
93
- def visit_Arel_Table o, collector
125
+ def visit_Arel_Table(o, collector)
94
126
  # Apparently, o.engine.connection can actually be a different adapter
95
127
  # than sqlserver. Can be removed if fixed in ActiveRecord. See:
96
128
  # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450
@@ -112,7 +144,7 @@ module Arel
112
144
  end
113
145
  end
114
146
 
115
- def visit_Arel_Nodes_JoinSource o, collector
147
+ def visit_Arel_Nodes_JoinSource(o, collector)
116
148
  if o.left
117
149
  collector = visit o.left, collector
118
150
  collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector
@@ -124,7 +156,7 @@ module Arel
124
156
  collector
125
157
  end
126
158
 
127
- def visit_Arel_Nodes_InnerJoin o, collector
159
+ def visit_Arel_Nodes_InnerJoin(o, collector)
128
160
  if o.left.is_a?(Arel::Nodes::As) && o.left.left.is_a?(Arel::Nodes::Lateral)
129
161
  collector << "CROSS "
130
162
  visit o.left, collector
@@ -141,7 +173,7 @@ module Arel
141
173
  end
142
174
  end
143
175
 
144
- def visit_Arel_Nodes_OuterJoin o, collector
176
+ def visit_Arel_Nodes_OuterJoin(o, collector)
145
177
  if o.left.is_a?(Arel::Nodes::As) && o.left.left.is_a?(Arel::Nodes::Lateral)
146
178
  collector << "OUTER "
147
179
  visit o.left, collector
@@ -154,11 +186,11 @@ module Arel
154
186
  end
155
187
  end
156
188
 
157
- def collect_in_clause(left, right, collector)
158
- if Array === right
159
- right.each { |node| remove_invalid_ordering_from_select_statement(node) }
189
+ def visit_Arel_Nodes_In(o, collector)
190
+ if Array === o.right
191
+ o.right.each { |node| remove_invalid_ordering_from_select_statement(node) }
160
192
  else
161
- remove_invalid_ordering_from_select_statement(right)
193
+ remove_invalid_ordering_from_select_statement(o.right)
162
194
  end
163
195
 
164
196
  super
@@ -170,7 +202,7 @@ module Arel
170
202
 
171
203
  # SQLServer ToSql/Visitor (Additions)
172
204
 
173
- def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {}
205
+ def visit_Arel_Nodes_SelectStatement_SQLServer_Lock(collector, options = {})
174
206
  if select_statement_lock?
175
207
  collector = visit @select_statement.lock, collector
176
208
  collector << " " if options[:space]
@@ -178,7 +210,7 @@ module Arel
178
210
  collector
179
211
  end
180
212
 
181
- def visit_Orders_And_Let_Fetch_Happen o, collector
213
+ def visit_Orders_And_Let_Fetch_Happen(o, collector)
182
214
  make_Fetch_Possible_And_Deterministic o
183
215
  unless o.orders.empty?
184
216
  collector << " ORDER BY "
@@ -191,14 +223,14 @@ module Arel
191
223
  collector
192
224
  end
193
225
 
194
- def visit_Make_Fetch_Happen o, collector
226
+ def visit_Make_Fetch_Happen(o, collector)
195
227
  o.offset = Nodes::Offset.new(0) if o.limit && !o.offset
196
228
  collector = visit o.offset, collector if o.offset
197
229
  collector = visit o.limit, collector if o.limit
198
230
  collector
199
231
  end
200
232
 
201
- def visit_Arel_Nodes_Lateral o, collector
233
+ def visit_Arel_Nodes_Lateral(o, collector)
202
234
  collector << "APPLY"
203
235
  collector << " "
204
236
  if o.expr.is_a?(Arel::Nodes::SelectStatement)
@@ -226,7 +258,7 @@ module Arel
226
258
  @select_statement && @select_statement.lock
227
259
  end
228
260
 
229
- def make_Fetch_Possible_And_Deterministic o
261
+ def make_Fetch_Possible_And_Deterministic(o)
230
262
  return if o.limit.nil? && o.offset.nil?
231
263
 
232
264
  t = table_From_Statement o
@@ -239,7 +271,7 @@ module Arel
239
271
  end
240
272
  end
241
273
 
242
- def distinct_One_As_One_Is_So_Not_Fetch o
274
+ def distinct_One_As_One_Is_So_Not_Fetch(o)
243
275
  core = o.cores.first
244
276
  distinct = Nodes::Distinct === core.set_quantifier
245
277
  oneasone = core.projections.all? { |x| x == ActiveRecord::FinderMethods::ONE_AS_ONE }
@@ -250,7 +282,7 @@ module Arel
250
282
  end
251
283
  end
252
284
 
253
- def table_From_Statement o
285
+ def table_From_Statement(o)
254
286
  core = o.cores.first
255
287
  if Arel::Table === core.from
256
288
  core.from
@@ -261,15 +293,15 @@ module Arel
261
293
  end
262
294
  end
263
295
 
264
- def primary_Key_From_Table t
296
+ def primary_Key_From_Table(t)
265
297
  return unless t
266
298
 
267
299
  column_name = @connection.schema_cache.primary_keys(t.name) ||
268
- @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
300
+ @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
269
301
  column_name ? t[column_name] : nil
270
302
  end
271
303
 
272
- def remote_server_table_name o
304
+ def remote_server_table_name(o)
273
305
  ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers(
274
306
  "#{o.class.engine.connection.database_prefix}#{o.name}"
275
307
  ).quoted
@@ -53,7 +53,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
53
53
  assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists."
54
54
 
55
55
  # Test when database and owner included in table name.
56
- Topic.table_name = "#{ActiveRecord::Base.configurations["arunit"]['database']}.dbo.topics"
56
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
57
+ Topic.table_name = "#{db_config.database}.dbo.topics"
57
58
  assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists."
58
59
  ensure
59
60
  Topic.table_name = "topics"
@@ -64,8 +65,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
64
65
  arunit_connection = Topic.connection
65
66
  arunit2_connection = College.connection
66
67
 
67
- arunit_database = arunit_connection.pool.spec.config[:database]
68
- arunit2_database = arunit2_connection.pool.spec.config[:database]
68
+ arunit_database = arunit_connection.pool.db_config.database
69
+ arunit2_database = arunit2_connection.pool.db_config.database
69
70
 
70
71
  # Assert that connections use different default databases schemas.
71
72
  assert_not_equal arunit_database, arunit2_database
@@ -100,21 +101,23 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
100
101
 
101
102
  it "test bad connection" do
102
103
  assert_raise ActiveRecord::NoDatabaseError do
103
- config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest")
104
- ActiveRecord::Base.sqlserver_connection config
104
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
105
+ configuration = db_config.configuration_hash.merge(database: "inexistent_activerecord_unittest")
106
+ ActiveRecord::Base.sqlserver_connection configuration
105
107
  end
106
108
  end
107
109
 
108
110
  it "test database exists returns false if database does not exist" do
109
- config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest")
110
- assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config),
111
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
112
+ configuration = db_config.configuration_hash.merge(database: "inexistent_activerecord_unittest")
113
+ assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(configuration),
111
114
  "expected database to not exist"
112
115
  end
113
116
 
114
117
  it "test database exists returns true when the database exists" do
115
- config = ActiveRecord::Base.configurations["arunit"]
116
- assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config),
117
- "expected database #{config[:database]} to exist"
118
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary")
119
+ assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(db_config.configuration_hash),
120
+ "expected database #{db_config.database} to exist"
118
121
  end
119
122
 
120
123
  describe "with different language" do
@@ -466,12 +469,11 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
466
469
  describe "block writes to a database" do
467
470
  def setup
468
471
  @conn = ActiveRecord::Base.connection
469
- @connection_handler = ActiveRecord::Base.connection_handler
470
472
  end
471
473
 
472
474
  def test_errors_when_an_insert_query_is_called_while_preventing_writes
473
475
  assert_raises(ActiveRecord::ReadOnlyError) do
474
- @connection_handler.while_preventing_writes do
476
+ ActiveRecord::Base.while_preventing_writes do
475
477
  @conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
476
478
  end
477
479
  end
@@ -481,7 +483,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
481
483
  @conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
482
484
 
483
485
  assert_raises(ActiveRecord::ReadOnlyError) do
484
- @connection_handler.while_preventing_writes do
486
+ ActiveRecord::Base.while_preventing_writes do
485
487
  @conn.update("UPDATE [subscribers] SET [subscribers].[name] = 'Aidan' WHERE [subscribers].[nick] = 'aido'")
486
488
  end
487
489
  end
@@ -491,7 +493,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
491
493
  @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
492
494
 
493
495
  assert_raises(ActiveRecord::ReadOnlyError) do
494
- @connection_handler.while_preventing_writes do
496
+ ActiveRecord::Base.while_preventing_writes do
495
497
  @conn.execute("DELETE FROM [subscribers] WHERE [subscribers].[nick] = 'aido'")
496
498
  end
497
499
  end
@@ -500,7 +502,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
500
502
  def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
501
503
  @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
502
504
 
503
- @connection_handler.while_preventing_writes do
505
+ ActiveRecord::Base.while_preventing_writes do
504
506
  assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'")
505
507
  end
506
508
  end