activerecord-sqlserver-adapter 6.0.3 → 6.1.0.0.rc1

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