activerecord-sqlserver-adapter 6.0.0.rc1 → 6.1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +26 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +29 -0
  5. data/CHANGELOG.md +18 -23
  6. data/Gemfile +11 -5
  7. data/Guardfile +9 -8
  8. data/README.md +32 -3
  9. data/RUNNING_UNIT_TESTS.md +1 -1
  10. data/Rakefile +12 -16
  11. data/VERSION +1 -1
  12. data/activerecord-sqlserver-adapter.gemspec +4 -4
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +0 -4
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -4
  15. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +3 -13
  16. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +8 -5
  17. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +2 -3
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +2 -3
  19. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
  20. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +58 -43
  21. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +7 -12
  22. data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -3
  23. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +15 -15
  24. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -3
  25. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -10
  26. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +131 -105
  27. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +6 -8
  28. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
  29. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
  30. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +25 -7
  31. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +1 -5
  32. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +6 -10
  33. data/lib/active_record/connection_adapters/sqlserver/type.rb +36 -35
  34. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -2
  35. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +0 -2
  36. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +0 -2
  37. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +2 -2
  38. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +0 -2
  39. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +2 -3
  40. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +2 -3
  41. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +0 -2
  42. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +0 -2
  43. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +0 -2
  44. data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +0 -2
  46. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +0 -2
  47. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +0 -1
  48. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +0 -2
  49. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +0 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +0 -2
  51. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +0 -2
  52. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +0 -2
  53. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +0 -2
  54. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +0 -2
  55. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +2 -3
  56. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +6 -9
  57. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +0 -2
  58. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +0 -2
  59. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +1 -3
  60. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +0 -2
  61. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +0 -2
  62. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +0 -2
  63. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +0 -2
  64. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +1 -2
  65. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +1 -3
  66. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +0 -2
  67. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +1 -3
  68. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +0 -2
  69. data/lib/active_record/connection_adapters/sqlserver/utils.rb +8 -11
  70. data/lib/active_record/connection_adapters/sqlserver/version.rb +0 -2
  71. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +170 -136
  72. data/lib/active_record/connection_adapters/sqlserver_column.rb +16 -1
  73. data/lib/active_record/sqlserver_base.rb +9 -15
  74. data/lib/active_record/tasks/sqlserver_database_tasks.rb +36 -39
  75. data/lib/activerecord-sqlserver-adapter.rb +1 -1
  76. data/lib/arel/visitors/sqlserver.rb +126 -50
  77. data/lib/arel_sqlserver.rb +2 -2
  78. data/test/cases/adapter_test_sqlserver.rb +203 -190
  79. data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
  80. data/test/cases/change_column_null_test_sqlserver.rb +12 -12
  81. data/test/cases/coerced_tests.rb +656 -318
  82. data/test/cases/column_test_sqlserver.rb +285 -284
  83. data/test/cases/connection_test_sqlserver.rb +15 -20
  84. data/test/cases/disconnected_test_sqlserver.rb +39 -0
  85. data/test/cases/execute_procedure_test_sqlserver.rb +26 -19
  86. data/test/cases/fetch_test_sqlserver.rb +14 -22
  87. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +12 -18
  88. data/test/cases/helper_sqlserver.rb +13 -15
  89. data/test/cases/in_clause_test_sqlserver.rb +36 -9
  90. data/test/cases/index_test_sqlserver.rb +13 -15
  91. data/test/cases/json_test_sqlserver.rb +23 -25
  92. data/test/cases/lateral_test_sqlserver.rb +35 -0
  93. data/test/cases/migration_test_sqlserver.rb +71 -26
  94. data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
  95. data/test/cases/order_test_sqlserver.rb +57 -53
  96. data/test/cases/pessimistic_locking_test_sqlserver.rb +25 -33
  97. data/test/cases/primary_keys_test_sqlserver.rb +103 -0
  98. data/test/cases/rake_test_sqlserver.rb +33 -46
  99. data/test/cases/schema_dumper_test_sqlserver.rb +121 -108
  100. data/test/cases/schema_test_sqlserver.rb +18 -26
  101. data/test/cases/scratchpad_test_sqlserver.rb +2 -4
  102. data/test/cases/showplan_test_sqlserver.rb +24 -33
  103. data/test/cases/specific_schema_test_sqlserver.rb +66 -65
  104. data/test/cases/transaction_test_sqlserver.rb +16 -19
  105. data/test/cases/trigger_test_sqlserver.rb +12 -12
  106. data/test/cases/utils_test_sqlserver.rb +68 -70
  107. data/test/cases/uuid_test_sqlserver.rb +11 -13
  108. data/test/debug.rb +6 -6
  109. data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
  110. data/test/migrations/create_clients_and_change_column_null.rb +1 -1
  111. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +2 -4
  112. data/test/models/sqlserver/booking.rb +1 -1
  113. data/test/models/sqlserver/customers_view.rb +1 -1
  114. data/test/models/sqlserver/dollar_table_name.rb +1 -1
  115. data/test/models/sqlserver/edge_schema.rb +1 -3
  116. data/test/models/sqlserver/fk_has_fk.rb +1 -1
  117. data/test/models/sqlserver/fk_has_pk.rb +1 -1
  118. data/test/models/sqlserver/natural_pk_data.rb +2 -2
  119. data/test/models/sqlserver/natural_pk_int_data.rb +1 -1
  120. data/test/models/sqlserver/no_pk_data.rb +1 -1
  121. data/test/models/sqlserver/object_default.rb +1 -1
  122. data/test/models/sqlserver/quoted_table.rb +2 -2
  123. data/test/models/sqlserver/quoted_view_1.rb +1 -1
  124. data/test/models/sqlserver/quoted_view_2.rb +1 -1
  125. data/test/models/sqlserver/sst_memory.rb +1 -1
  126. data/test/models/sqlserver/sst_string_collation.rb +3 -0
  127. data/test/models/sqlserver/string_default.rb +1 -1
  128. data/test/models/sqlserver/string_defaults_big_view.rb +1 -1
  129. data/test/models/sqlserver/string_defaults_view.rb +1 -1
  130. data/test/models/sqlserver/tinyint_pk.rb +1 -1
  131. data/test/models/sqlserver/trigger.rb +2 -2
  132. data/test/models/sqlserver/trigger_history.rb +1 -1
  133. data/test/models/sqlserver/upper.rb +1 -1
  134. data/test/models/sqlserver/uppered.rb +1 -1
  135. data/test/models/sqlserver/uuid.rb +1 -1
  136. data/test/schema/sqlserver_specific_schema.rb +36 -21
  137. data/test/support/coerceable_test_sqlserver.rb +1 -4
  138. data/test/support/connection_reflection.rb +1 -2
  139. data/test/support/core_ext/query_cache.rb +1 -1
  140. data/test/support/load_schema_sqlserver.rb +3 -5
  141. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
  142. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
  143. data/test/support/minitest_sqlserver.rb +1 -1
  144. data/test/support/paths_sqlserver.rb +9 -11
  145. data/test/support/rake_helpers.rb +12 -10
  146. data/test/support/sql_counter_sqlserver.rb +14 -16
  147. data/test/support/test_in_memory_oltp.rb +7 -7
  148. metadata +31 -11
  149. data/.travis.yml +0 -23
@@ -5,8 +5,7 @@ module ActiveRecord
5
5
  module SQLServer
6
6
  module CoreExt
7
7
  module Explain
8
-
9
- SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql '
8
+ SQLSERVER_STATEMENT_PREFIX = "EXEC sp_executesql "
10
9
  SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/
11
10
 
12
11
  def exec_explain(queries)
@@ -22,19 +21,23 @@ module ActiveRecord
22
21
  # which uses sp_executesql to just the first argument, then unquote it. Likewise our
23
22
  # `sp_executesql` method should substitude the @n args with the quoted values.
24
23
  def unprepare_sqlserver_statement(sql, binds)
25
- return sql unless sql.starts_with?(SQLSERVER_STATEMENT_PREFIX)
24
+ return sql unless sql.start_with?(SQLSERVER_STATEMENT_PREFIX)
26
25
 
27
26
  executesql = sql.from(SQLSERVER_STATEMENT_PREFIX.length)
28
27
  executesql = executesql.match(SQLSERVER_STATEMENT_REGEXP).to_a[1]
29
28
 
30
29
  binds.each_with_index do |bind, index|
31
- value = connection.quote(bind)
30
+
31
+ value = if bind.is_a?(::ActiveModel::Attribute) then
32
+ connection.quote(bind.value_for_database)
33
+ else
34
+ connection.quote(bind)
35
+ end
32
36
  executesql = executesql.sub("@#{index}", value)
33
37
  end
34
38
 
35
39
  executesql
36
40
  end
37
-
38
41
  end
39
42
  end
40
43
  end
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_record/relation'
4
- require 'active_record/version'
3
+ require "active_record/relation"
4
+ require "active_record/version"
5
5
 
6
6
  module ActiveRecord
7
7
  module ConnectionAdapters
8
8
  module SQLServer
9
9
  module CoreExt
10
10
  module FinderMethods
11
-
12
11
  private
13
12
 
14
13
  # Same as original except we order by values in distinct select if present.
@@ -1,14 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_record/relation'
4
- require 'active_record/version'
3
+ require "active_record/relation"
4
+ require "active_record/version"
5
5
 
6
6
  module ActiveRecord
7
7
  module ConnectionAdapters
8
8
  module SQLServer
9
9
  module CoreExt
10
10
  module QueryMethods
11
-
12
11
  private
13
12
 
14
13
  # Copy of original from Rails master.
@@ -37,10 +37,6 @@ module ActiveRecord
37
37
  end
38
38
  deprecate :columns_per_multicolumn_index
39
39
 
40
- def in_clause_length
41
- 10_000
42
- end
43
-
44
40
  def sql_query_length
45
41
  65_536 * 4_096
46
42
  end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  module DatabaseStatements
7
- READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback) # :nodoc:
7
+ READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback, :waitfor) # :nodoc:
8
8
  private_constant :READ_QUERY
9
9
 
10
10
  def write_query?(sql) # :nodoc:
@@ -17,6 +17,7 @@ module ActiveRecord
17
17
  end
18
18
 
19
19
  materialize_transactions
20
+ mark_transaction_written_if_write(sql)
20
21
 
21
22
  if id_insert_table_name = query_requires_identity_insert?(sql)
22
23
  with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) }
@@ -25,12 +26,13 @@ module ActiveRecord
25
26
  end
26
27
  end
27
28
 
28
- def exec_query(sql, name = 'SQL', binds = [], prepare: false)
29
+ def exec_query(sql, name = "SQL", binds = [], prepare: false)
29
30
  if preventing_writes? && write_query?(sql)
30
31
  raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
31
32
  end
32
33
 
33
34
  materialize_transactions
35
+ mark_transaction_written_if_write(sql)
34
36
 
35
37
  sp_executesql(sql, name, binds, prepare: prepare)
36
38
  end
@@ -44,17 +46,17 @@ module ActiveRecord
44
46
  end
45
47
 
46
48
  def exec_delete(sql, name, binds)
47
- sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows'
49
+ sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows"
48
50
  super(sql, name, binds).rows.first.first
49
51
  end
50
52
 
51
53
  def exec_update(sql, name, binds)
52
- sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows'
54
+ sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows"
53
55
  super(sql, name, binds).rows.first.first
54
56
  end
55
57
 
56
58
  def begin_db_transaction
57
- do_execute 'BEGIN TRANSACTION'
59
+ do_execute "BEGIN TRANSACTION", "TRANSACTION"
58
60
  end
59
61
 
60
62
  def transaction_isolation_levels
@@ -67,25 +69,25 @@ module ActiveRecord
67
69
  end
68
70
 
69
71
  def set_transaction_isolation_level(isolation_level)
70
- do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
72
+ do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}", "TRANSACTION"
71
73
  end
72
74
 
73
75
  def commit_db_transaction
74
- do_execute 'COMMIT TRANSACTION'
76
+ do_execute "COMMIT TRANSACTION", "TRANSACTION"
75
77
  end
76
78
 
77
79
  def exec_rollback_db_transaction
78
- do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
80
+ do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION", "TRANSACTION"
79
81
  end
80
82
 
81
83
  include Savepoints
82
84
 
83
85
  def create_savepoint(name = current_savepoint_name)
84
- do_execute "SAVE TRANSACTION #{name}"
86
+ do_execute "SAVE TRANSACTION #{name}", "TRANSACTION"
85
87
  end
86
88
 
87
89
  def exec_rollback_to_savepoint(name = current_savepoint_name)
88
- do_execute "ROLLBACK TRANSACTION #{name}"
90
+ do_execute "ROLLBACK TRANSACTION #{name}", "TRANSACTION"
89
91
  end
90
92
 
91
93
  def release_savepoint(name = current_savepoint_name)
@@ -143,7 +145,7 @@ module ActiveRecord
143
145
  sql = +"INSERT #{insert.into}"
144
146
 
145
147
  if returning = insert.send(:insert_all).returning
146
- sql << " OUTPUT " << returning.map {|column| "INSERTED.#{quote_column_name(column)}" }.join(", ")
148
+ sql << " OUTPUT " << returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ")
147
149
  end
148
150
 
149
151
  sql << " #{insert.values_list}"
@@ -159,13 +161,13 @@ module ActiveRecord
159
161
  variables.first.map { |k, v| "@#{k} = #{quote(v)}" }
160
162
  else
161
163
  variables.map { |v| quote(v) }
162
- end.join(', ')
164
+ end.join(", ")
163
165
  sql = "EXEC #{proc_name} #{vars}".strip
164
- name = 'Execute Procedure'
166
+ name = "Execute Procedure"
165
167
  log(sql, name) do
166
168
  case @connection_options[:mode]
167
169
  when :dblib
168
- result = @connection.execute(sql)
170
+ result = ensure_established_connection! { dblib_execute(sql) }
169
171
  options = { as: :hash, cache_rows: true, timezone: ActiveRecord::Base.default_timezone || :utc }
170
172
  result.each(options) do |row|
171
173
  r = row.with_indifferent_access
@@ -186,20 +188,22 @@ module ActiveRecord
186
188
 
187
189
  def use_database(database = nil)
188
190
  return if sqlserver_azure?
191
+
189
192
  name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]).quoted
190
193
  do_execute "USE #{name}" unless name.blank?
191
194
  end
192
195
 
193
196
  def user_options
194
197
  return {} if sqlserver_azure?
195
- rows = select_rows('DBCC USEROPTIONS WITH NO_INFOMSGS', 'SCHEMA')
198
+
199
+ rows = select_rows("DBCC USEROPTIONS WITH NO_INFOMSGS", "SCHEMA")
196
200
  rows = rows.first if rows.size == 2 && rows.last.empty?
197
201
  rows.reduce(HashWithIndifferentAccess.new) do |values, row|
198
202
  if row.instance_of? Hash
199
- set_option = row.values[0].gsub(/\s+/, '_')
203
+ set_option = row.values[0].gsub(/\s+/, "_")
200
204
  user_value = row.values[1]
201
- elsif row.instance_of? Array
202
- set_option = row[0].gsub(/\s+/, '_')
205
+ elsif row.instance_of? Array
206
+ set_option = row[0].gsub(/\s+/, "_")
203
207
  user_value = row[1]
204
208
  end
205
209
  values[set_option] = user_value
@@ -209,9 +213,9 @@ module ActiveRecord
209
213
 
210
214
  def user_options_dateformat
211
215
  if sqlserver_azure?
212
- select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA'
216
+ select_value "SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID", "SCHEMA"
213
217
  else
214
- user_options['dateformat']
218
+ user_options["dateformat"]
215
219
  end
216
220
  end
217
221
 
@@ -226,29 +230,28 @@ module ActiveRecord
226
230
  WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level]
227
231
  FROM [sys].[dm_exec_sessions]
228
232
  WHERE [session_id] = @@SPID).squish
229
- select_value sql, 'SCHEMA'
233
+ select_value sql, "SCHEMA"
230
234
  else
231
- user_options['isolation_level']
235
+ user_options["isolation_level"]
232
236
  end
233
237
  end
234
238
 
235
239
  def user_options_language
236
240
  if sqlserver_azure?
237
- select_value 'SELECT @@LANGUAGE AS [language]', 'SCHEMA'
241
+ select_value "SELECT @@LANGUAGE AS [language]", "SCHEMA"
238
242
  else
239
- user_options['language']
243
+ user_options["language"]
240
244
  end
241
245
  end
242
246
 
243
247
  def newid_function
244
- select_value 'SELECT NEWID()'
248
+ select_value "SELECT NEWID()"
245
249
  end
246
250
 
247
251
  def newsequentialid_function
248
- select_value 'SELECT NEWSEQUENTIALID()'
252
+ select_value "SELECT NEWSEQUENTIALID()"
249
253
  end
250
254
 
251
-
252
255
  protected
253
256
 
254
257
  def sql_for_insert(sql, pk, binds)
@@ -263,7 +266,7 @@ module ActiveRecord
263
266
  exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql)
264
267
 
265
268
  if exclude_output_inserted
266
- id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? 'bigint' : exclude_output_inserted
269
+ id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted
267
270
  <<~SQL.squish
268
271
  DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type});
269
272
  #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"}
@@ -288,8 +291,9 @@ module ActiveRecord
288
291
 
289
292
  # === SQLServer Specific (Executing) ============================ #
290
293
 
291
- def do_execute(sql, name = 'SQL')
294
+ def do_execute(sql, name = "SQL")
292
295
  materialize_transactions
296
+ mark_transaction_written_if_write(sql)
293
297
 
294
298
  log(sql, name) { raw_connection_do(sql) }
295
299
  end
@@ -316,11 +320,12 @@ module ActiveRecord
316
320
 
317
321
  def sp_executesql_sql_type(attr)
318
322
  return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type)
323
+
319
324
  case value = attr.value_for_database
320
325
  when Numeric
321
- value > 2_147_483_647 ? 'bigint'.freeze : 'int'.freeze
326
+ value > 2_147_483_647 ? "bigint".freeze : "int".freeze
322
327
  else
323
- 'nvarchar(max)'.freeze
328
+ "nvarchar(max)".freeze
324
329
  end
325
330
  end
326
331
 
@@ -335,14 +340,14 @@ module ActiveRecord
335
340
  end
336
341
 
337
342
  def sp_executesql_sql(sql, types, params, name)
338
- if name == 'EXPLAIN'
343
+ if name == "EXPLAIN"
339
344
  params.each.with_index do |param, index|
340
345
  substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values.
341
346
  sql = sql.sub substitute_at_finder, param.to_s
342
347
  end
343
348
  else
344
- types = quote(types.join(', '))
345
- params = params.map.with_index{ |p, i| "@#{i} = #{p}" }.join(', ') # Only p is needed, but with @i helps explain regexp.
349
+ types = quote(types.join(", "))
350
+ params = params.map.with_index { |p, i| "@#{i} = #{p}" }.join(", ") # Only p is needed, but with @i helps explain regexp.
346
351
  sql = "EXEC sp_executesql #{quote(sql)}"
347
352
  sql += ", #{types}, #{params}" unless params.empty?
348
353
  end
@@ -352,13 +357,7 @@ module ActiveRecord
352
357
  def raw_connection_do(sql)
353
358
  case @connection_options[:mode]
354
359
  when :dblib
355
- result = @connection.execute(sql)
356
-
357
- # TinyTDS returns false instead of raising an exception if connection fails.
358
- # Getting around this by raising an exception ourselves while this PR
359
- # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
360
- raise TinyTds::Error, 'failed to execute statement' if result.is_a?(FalseClass)
361
-
360
+ result = ensure_established_connection! { dblib_execute(sql) }
362
361
  result.do
363
362
  end
364
363
  ensure
@@ -377,8 +376,10 @@ module ActiveRecord
377
376
 
378
377
  def exclude_output_inserted_table_name?(table_name, sql)
379
378
  return false unless exclude_output_inserted_table_names?
379
+
380
380
  table_name ||= get_table_name(sql)
381
381
  return false unless table_name
382
+
382
383
  self.class.exclude_output_inserted_table_names[table_name]
383
384
  end
384
385
 
@@ -407,7 +408,7 @@ module ActiveRecord
407
408
 
408
409
  # === SQLServer Specific (Selecting) ============================ #
409
410
 
410
- def raw_select(sql, name = 'SQL', binds = [], options = {})
411
+ def raw_select(sql, name = "SQL", binds = [], options = {})
411
412
  log(sql, name, binds) { _raw_select(sql, options) }
412
413
  end
413
414
 
@@ -421,7 +422,7 @@ module ActiveRecord
421
422
  def raw_connection_run(sql)
422
423
  case @connection_options[:mode]
423
424
  when :dblib
424
- @connection.execute(sql)
425
+ ensure_established_connection! { dblib_execute(sql) }
425
426
  end
426
427
  end
427
428
 
@@ -456,6 +457,20 @@ module ActiveRecord
456
457
  handle
457
458
  end
458
459
 
460
+ def dblib_execute(sql)
461
+ @connection.execute(sql).tap do |result|
462
+ # TinyTDS returns false instead of raising an exception if connection fails.
463
+ # Getting around this by raising an exception ourselves while this PR
464
+ # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
465
+ raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass)
466
+ end
467
+ end
468
+
469
+ def ensure_established_connection!
470
+ raise TinyTds::Error, 'SQL Server client is not connected' unless @connection
471
+
472
+ yield
473
+ end
459
474
  end
460
475
  end
461
476
  end
@@ -4,7 +4,6 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  module DatabaseTasks
7
-
8
7
  def create_database(database, options = {})
9
8
  name = SQLServer::Utils.extract_identifiers(database)
10
9
  db_options = create_database_options(options)
@@ -19,7 +18,7 @@ module ActiveRecord
19
18
  end
20
19
 
21
20
  def current_database
22
- select_value 'SELECT DB_NAME()'
21
+ select_value "SELECT DB_NAME()"
23
22
  end
24
23
 
25
24
  def charset
@@ -32,20 +31,20 @@ module ActiveRecord
32
31
 
33
32
  private
34
33
 
35
- def create_database_options(options={})
34
+ def create_database_options(options = {})
36
35
  keys = [:collate]
37
36
  copts = @connection_options
38
37
  options = {
39
38
  collate: copts[:collation]
40
39
  }.merge(options.symbolize_keys).select { |_, v|
41
40
  v.present?
42
- }.slice(*keys).map { |k,v|
41
+ }.slice(*keys).map { |k, v|
43
42
  "#{k.to_s.upcase} #{v}"
44
- }.join(' ')
43
+ }.join(" ")
45
44
  options
46
45
  end
47
46
 
48
- def create_database_edition_options(options={})
47
+ def create_database_edition_options(options = {})
49
48
  keys = [:maxsize, :edition, :service_objective]
50
49
  copts = @connection_options
51
50
  edition_options = {
@@ -54,17 +53,13 @@ module ActiveRecord
54
53
  service_objective: copts[:azure_service_objective]
55
54
  }.merge(options.symbolize_keys).select { |_, v|
56
55
  v.present?
57
- }.slice(*keys).map { |k,v|
56
+ }.slice(*keys).map { |k, v|
58
57
  "#{k.to_s.upcase} = #{v}"
59
- }.join(', ')
58
+ }.join(", ")
60
59
  edition_options = "( #{edition_options} )" if edition_options.present?
61
60
  edition_options
62
61
  end
63
-
64
62
  end
65
63
  end
66
64
  end
67
65
  end
68
-
69
-
70
-
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
-
5
4
  class DeadlockVictim < WrappedDatabaseException
6
5
  end
7
-
8
-
9
6
  end