activerecord-sqlserver-adapter 5.2.1 → 6.0.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.
- checksums.yaml +4 -4
- data/.editorconfig +9 -0
- data/.github/issue_template.md +23 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +29 -0
- data/.travis.yml +6 -8
- data/CHANGELOG.md +38 -24
- data/{Dockerfile → Dockerfile.ci} +1 -1
- data/Gemfile +48 -41
- data/Guardfile +9 -8
- data/README.md +9 -30
- data/RUNNING_UNIT_TESTS.md +3 -0
- data/Rakefile +14 -16
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +25 -14
- data/appveyor.yml +24 -17
- data/docker-compose.ci.yml +7 -5
- data/guides/RELEASING.md +11 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +8 -7
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +36 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +88 -44
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +9 -12
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +46 -8
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +16 -5
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +190 -164
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -44
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +7 -9
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +4 -3
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type.rb +37 -35
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -11
- data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +128 -92
- data/lib/active_record/connection_adapters/sqlserver_column.rb +9 -5
- data/lib/active_record/sqlserver_base.rb +9 -1
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +28 -32
- data/lib/activerecord-sqlserver-adapter.rb +3 -1
- data/lib/arel/visitors/sqlserver.rb +58 -24
- data/lib/arel_sqlserver.rb +4 -2
- data/test/appveyor/dbsetup.ps1 +4 -4
- data/test/cases/adapter_test_sqlserver.rb +214 -171
- data/test/cases/change_column_null_test_sqlserver.rb +14 -12
- data/test/cases/coerced_tests.rb +631 -356
- data/test/cases/column_test_sqlserver.rb +283 -284
- data/test/cases/connection_test_sqlserver.rb +17 -20
- data/test/cases/execute_procedure_test_sqlserver.rb +20 -20
- data/test/cases/fetch_test_sqlserver.rb +16 -22
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
- data/test/cases/helper_sqlserver.rb +15 -15
- data/test/cases/in_clause_test_sqlserver.rb +36 -0
- data/test/cases/index_test_sqlserver.rb +15 -15
- data/test/cases/json_test_sqlserver.rb +25 -25
- data/test/cases/migration_test_sqlserver.rb +25 -29
- data/test/cases/order_test_sqlserver.rb +53 -54
- data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
- data/test/cases/rake_test_sqlserver.rb +33 -45
- data/test/cases/schema_dumper_test_sqlserver.rb +107 -109
- data/test/cases/schema_test_sqlserver.rb +20 -26
- data/test/cases/scratchpad_test_sqlserver.rb +4 -4
- data/test/cases/showplan_test_sqlserver.rb +28 -35
- data/test/cases/specific_schema_test_sqlserver.rb +68 -65
- data/test/cases/transaction_test_sqlserver.rb +18 -20
- data/test/cases/trigger_test_sqlserver.rb +14 -13
- data/test/cases/utils_test_sqlserver.rb +70 -70
- data/test/cases/uuid_test_sqlserver.rb +13 -14
- data/test/debug.rb +8 -6
- data/test/migrations/create_clients_and_change_column_null.rb +3 -1
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
- data/test/models/sqlserver/booking.rb +3 -1
- data/test/models/sqlserver/customers_view.rb +3 -1
- data/test/models/sqlserver/datatype.rb +2 -0
- data/test/models/sqlserver/datatype_migration.rb +2 -0
- data/test/models/sqlserver/dollar_table_name.rb +3 -1
- data/test/models/sqlserver/edge_schema.rb +3 -3
- data/test/models/sqlserver/fk_has_fk.rb +3 -1
- data/test/models/sqlserver/fk_has_pk.rb +3 -1
- data/test/models/sqlserver/natural_pk_data.rb +4 -2
- data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
- data/test/models/sqlserver/no_pk_data.rb +3 -1
- data/test/models/sqlserver/object_default.rb +3 -1
- data/test/models/sqlserver/quoted_table.rb +4 -2
- data/test/models/sqlserver/quoted_view_1.rb +3 -1
- data/test/models/sqlserver/quoted_view_2.rb +3 -1
- data/test/models/sqlserver/sst_memory.rb +3 -1
- data/test/models/sqlserver/string_default.rb +3 -1
- data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
- data/test/models/sqlserver/string_defaults_view.rb +3 -1
- data/test/models/sqlserver/tinyint_pk.rb +3 -1
- data/test/models/sqlserver/trigger.rb +4 -2
- data/test/models/sqlserver/trigger_history.rb +3 -1
- data/test/models/sqlserver/upper.rb +3 -1
- data/test/models/sqlserver/uppered.rb +3 -1
- data/test/models/sqlserver/uuid.rb +3 -1
- data/test/schema/sqlserver_specific_schema.rb +22 -22
- data/test/support/coerceable_test_sqlserver.rb +15 -9
- data/test/support/connection_reflection.rb +3 -2
- data/test/support/core_ext/query_cache.rb +4 -1
- data/test/support/load_schema_sqlserver.rb +5 -5
- data/test/support/minitest_sqlserver.rb +3 -1
- data/test/support/paths_sqlserver.rb +11 -11
- data/test/support/rake_helpers.rb +13 -10
- data/test/support/sql_counter_sqlserver.rb +3 -4
- data/test/support/test_in_memory_oltp.rb +9 -7
- metadata +17 -7
|
@@ -1,37 +1,39 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
11
|
-
require
|
|
12
|
-
require
|
|
13
|
-
require
|
|
14
|
-
require
|
|
15
|
-
require
|
|
16
|
-
require
|
|
17
|
-
require
|
|
18
|
-
require
|
|
19
|
-
require
|
|
20
|
-
require
|
|
21
|
-
require
|
|
22
|
-
require
|
|
23
|
-
require
|
|
24
|
-
require
|
|
25
|
-
require
|
|
26
|
-
require
|
|
27
|
-
require
|
|
28
|
-
require
|
|
29
|
-
require
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "base64"
|
|
4
|
+
require "active_record"
|
|
5
|
+
require "arel_sqlserver"
|
|
6
|
+
require "active_record/connection_adapters/abstract_adapter"
|
|
7
|
+
require "active_record/connection_adapters/sqlserver/core_ext/active_record"
|
|
8
|
+
require "active_record/connection_adapters/sqlserver/core_ext/calculations"
|
|
9
|
+
require "active_record/connection_adapters/sqlserver/core_ext/explain"
|
|
10
|
+
require "active_record/connection_adapters/sqlserver/core_ext/explain_subscriber"
|
|
11
|
+
require "active_record/connection_adapters/sqlserver/core_ext/attribute_methods"
|
|
12
|
+
require "active_record/connection_adapters/sqlserver/core_ext/finder_methods"
|
|
13
|
+
require "active_record/connection_adapters/sqlserver/core_ext/query_methods"
|
|
14
|
+
require "active_record/connection_adapters/sqlserver/core_ext/preloader"
|
|
15
|
+
require "active_record/connection_adapters/sqlserver/version"
|
|
16
|
+
require "active_record/connection_adapters/sqlserver/type"
|
|
17
|
+
require "active_record/connection_adapters/sqlserver/database_limits"
|
|
18
|
+
require "active_record/connection_adapters/sqlserver/database_statements"
|
|
19
|
+
require "active_record/connection_adapters/sqlserver/database_tasks"
|
|
20
|
+
require "active_record/connection_adapters/sqlserver/transaction"
|
|
21
|
+
require "active_record/connection_adapters/sqlserver/errors"
|
|
22
|
+
require "active_record/connection_adapters/sqlserver/schema_creation"
|
|
23
|
+
require "active_record/connection_adapters/sqlserver/schema_dumper"
|
|
24
|
+
require "active_record/connection_adapters/sqlserver/schema_statements"
|
|
25
|
+
require "active_record/connection_adapters/sqlserver/sql_type_metadata"
|
|
26
|
+
require "active_record/connection_adapters/sqlserver/showplan"
|
|
27
|
+
require "active_record/connection_adapters/sqlserver/table_definition"
|
|
28
|
+
require "active_record/connection_adapters/sqlserver/quoting"
|
|
29
|
+
require "active_record/connection_adapters/sqlserver/utils"
|
|
30
|
+
require "active_record/sqlserver_base"
|
|
31
|
+
require "active_record/connection_adapters/sqlserver_column"
|
|
32
|
+
require "active_record/tasks/sqlserver_database_tasks"
|
|
30
33
|
|
|
31
34
|
module ActiveRecord
|
|
32
35
|
module ConnectionAdapters
|
|
33
36
|
class SQLServerAdapter < AbstractAdapter
|
|
34
|
-
|
|
35
37
|
include SQLServer::Version,
|
|
36
38
|
SQLServer::Quoting,
|
|
37
39
|
SQLServer::DatabaseStatements,
|
|
@@ -40,7 +42,7 @@ module ActiveRecord
|
|
|
40
42
|
SQLServer::DatabaseLimits,
|
|
41
43
|
SQLServer::DatabaseTasks
|
|
42
44
|
|
|
43
|
-
ADAPTER_NAME =
|
|
45
|
+
ADAPTER_NAME = "SQLServer".freeze
|
|
44
46
|
|
|
45
47
|
# Default precision for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql)
|
|
46
48
|
DEFAULT_TIME_PRECISION = 7
|
|
@@ -53,7 +55,7 @@ module ActiveRecord
|
|
|
53
55
|
cattr_accessor :showplan_option, instance_accessor: false
|
|
54
56
|
cattr_accessor :lowercase_schema_reflection
|
|
55
57
|
|
|
56
|
-
self.cs_equality_operator =
|
|
58
|
+
self.cs_equality_operator = "COLLATE Latin1_General_CS_AS_WS"
|
|
57
59
|
self.use_output_inserted = true
|
|
58
60
|
self.exclude_output_inserted_table_names = Concurrent::Map.new { false }
|
|
59
61
|
|
|
@@ -80,6 +82,12 @@ module ActiveRecord
|
|
|
80
82
|
SQLServer::SchemaCreation.new self
|
|
81
83
|
end
|
|
82
84
|
|
|
85
|
+
def self.database_exists?(config)
|
|
86
|
+
!!ActiveRecord::Base.sqlserver_connection(config)
|
|
87
|
+
rescue ActiveRecord::NoDatabaseError
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
|
|
83
91
|
def supports_ddl_transactions?
|
|
84
92
|
true
|
|
85
93
|
end
|
|
@@ -144,10 +152,30 @@ module ActiveRecord
|
|
|
144
152
|
true
|
|
145
153
|
end
|
|
146
154
|
|
|
155
|
+
def supports_lazy_transactions?
|
|
156
|
+
true
|
|
157
|
+
end
|
|
158
|
+
|
|
147
159
|
def supports_in_memory_oltp?
|
|
148
160
|
@version_year >= 2014
|
|
149
161
|
end
|
|
150
162
|
|
|
163
|
+
def supports_insert_returning?
|
|
164
|
+
true
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def supports_insert_on_duplicate_skip?
|
|
168
|
+
false
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def supports_insert_on_duplicate_update?
|
|
172
|
+
false
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def supports_insert_conflict_target?
|
|
176
|
+
false
|
|
177
|
+
end
|
|
178
|
+
|
|
151
179
|
def disable_referential_integrity
|
|
152
180
|
tables = tables_with_referential_integrity
|
|
153
181
|
tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" }
|
|
@@ -160,7 +188,8 @@ module ActiveRecord
|
|
|
160
188
|
|
|
161
189
|
def active?
|
|
162
190
|
return false unless @connection
|
|
163
|
-
|
|
191
|
+
|
|
192
|
+
raw_connection_do "SELECT 1"
|
|
164
193
|
true
|
|
165
194
|
rescue *connection_errors
|
|
166
195
|
false
|
|
@@ -190,13 +219,13 @@ module ActiveRecord
|
|
|
190
219
|
|
|
191
220
|
def reset!
|
|
192
221
|
reset_transaction
|
|
193
|
-
do_execute
|
|
222
|
+
do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"
|
|
194
223
|
end
|
|
195
224
|
|
|
196
225
|
# === Abstract Adapter (Misc Support) =========================== #
|
|
197
226
|
|
|
198
227
|
def tables_with_referential_integrity
|
|
199
|
-
schemas_and_tables = select_rows
|
|
228
|
+
schemas_and_tables = select_rows <<~SQL.squish
|
|
200
229
|
SELECT DISTINCT s.name, o.name
|
|
201
230
|
FROM sys.foreign_keys i
|
|
202
231
|
INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID
|
|
@@ -225,6 +254,7 @@ module ActiveRecord
|
|
|
225
254
|
|
|
226
255
|
def database_prefix_remote_server?
|
|
227
256
|
return false if database_prefix.blank?
|
|
257
|
+
|
|
228
258
|
name = SQLServer::Utils.extract_identifiers(database_prefix)
|
|
229
259
|
name.fully_qualified? && name.object.blank?
|
|
230
260
|
end
|
|
@@ -256,37 +286,40 @@ module ActiveRecord
|
|
|
256
286
|
result
|
|
257
287
|
end
|
|
258
288
|
|
|
289
|
+
def get_database_version # :nodoc:
|
|
290
|
+
version_year
|
|
291
|
+
end
|
|
259
292
|
|
|
260
293
|
protected
|
|
261
294
|
|
|
262
295
|
# === Abstract Adapter (Misc Support) =========================== #
|
|
263
296
|
|
|
264
297
|
def initialize_type_map(m = type_map)
|
|
265
|
-
m.register_type %r{.*},
|
|
298
|
+
m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
|
|
266
299
|
# Exact Numerics
|
|
267
|
-
register_class_with_limit m,
|
|
268
|
-
m.alias_type
|
|
269
|
-
register_class_with_limit m,
|
|
270
|
-
m.alias_type
|
|
271
|
-
m.alias_type
|
|
272
|
-
register_class_with_limit m,
|
|
273
|
-
m.alias_type
|
|
274
|
-
register_class_with_limit m,
|
|
275
|
-
m.alias_type
|
|
276
|
-
m.register_type
|
|
300
|
+
register_class_with_limit m, "bigint(8)", SQLServer::Type::BigInteger
|
|
301
|
+
m.alias_type "bigint", "bigint(8)"
|
|
302
|
+
register_class_with_limit m, "int(4)", SQLServer::Type::Integer
|
|
303
|
+
m.alias_type "integer", "int(4)"
|
|
304
|
+
m.alias_type "int", "int(4)"
|
|
305
|
+
register_class_with_limit m, "smallint(2)", SQLServer::Type::SmallInteger
|
|
306
|
+
m.alias_type "smallint", "smallint(2)"
|
|
307
|
+
register_class_with_limit m, "tinyint(1)", SQLServer::Type::TinyInteger
|
|
308
|
+
m.alias_type "tinyint", "tinyint(1)"
|
|
309
|
+
m.register_type "bit", SQLServer::Type::Boolean.new
|
|
277
310
|
m.register_type %r{\Adecimal}i do |sql_type|
|
|
278
311
|
scale = extract_scale(sql_type)
|
|
279
312
|
precision = extract_precision(sql_type)
|
|
280
313
|
SQLServer::Type::Decimal.new precision: precision, scale: scale
|
|
281
314
|
end
|
|
282
|
-
m.alias_type %r{\Anumeric}i,
|
|
283
|
-
m.register_type
|
|
284
|
-
m.register_type
|
|
315
|
+
m.alias_type %r{\Anumeric}i, "decimal"
|
|
316
|
+
m.register_type "money", SQLServer::Type::Money.new
|
|
317
|
+
m.register_type "smallmoney", SQLServer::Type::SmallMoney.new
|
|
285
318
|
# Approximate Numerics
|
|
286
|
-
m.register_type
|
|
287
|
-
m.register_type
|
|
319
|
+
m.register_type "float", SQLServer::Type::Float.new
|
|
320
|
+
m.register_type "real", SQLServer::Type::Real.new
|
|
288
321
|
# Date and Time
|
|
289
|
-
m.register_type
|
|
322
|
+
m.register_type "date", SQLServer::Type::Date.new
|
|
290
323
|
m.register_type %r{\Adatetime} do |sql_type|
|
|
291
324
|
precision = extract_precision(sql_type)
|
|
292
325
|
if precision
|
|
@@ -295,11 +328,11 @@ module ActiveRecord
|
|
|
295
328
|
SQLServer::Type::DateTime.new
|
|
296
329
|
end
|
|
297
330
|
end
|
|
298
|
-
m.register_type
|
|
331
|
+
m.register_type %r{\Adatetimeoffset}i do |sql_type|
|
|
299
332
|
precision = extract_precision(sql_type)
|
|
300
333
|
SQLServer::Type::DateTimeOffset.new precision: precision
|
|
301
334
|
end
|
|
302
|
-
m.register_type
|
|
335
|
+
m.register_type "smalldatetime", SQLServer::Type::SmallDateTime.new
|
|
303
336
|
m.register_type %r{\Atime}i do |sql_type|
|
|
304
337
|
precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION
|
|
305
338
|
SQLServer::Type::Time.new precision: precision
|
|
@@ -307,36 +340,38 @@ module ActiveRecord
|
|
|
307
340
|
# Character Strings
|
|
308
341
|
register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char
|
|
309
342
|
register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar
|
|
310
|
-
m.register_type
|
|
311
|
-
m.register_type
|
|
343
|
+
m.register_type "varchar(max)", SQLServer::Type::VarcharMax.new
|
|
344
|
+
m.register_type "text", SQLServer::Type::Text.new
|
|
312
345
|
# Unicode Character Strings
|
|
313
346
|
register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar
|
|
314
347
|
register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
|
|
315
|
-
m.alias_type
|
|
316
|
-
m.register_type
|
|
317
|
-
m.register_type
|
|
318
|
-
m.register_type
|
|
348
|
+
m.alias_type "string", "nvarchar(4000)"
|
|
349
|
+
m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
|
|
350
|
+
m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
|
|
351
|
+
m.register_type "ntext", SQLServer::Type::UnicodeText.new
|
|
319
352
|
# Binary Strings
|
|
320
353
|
register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
|
|
321
354
|
register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary
|
|
322
|
-
m.register_type
|
|
355
|
+
m.register_type "varbinary(max)", SQLServer::Type::VarbinaryMax.new
|
|
323
356
|
# Other Data Types
|
|
324
|
-
m.register_type
|
|
325
|
-
m.register_type
|
|
357
|
+
m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new
|
|
358
|
+
m.register_type "timestamp", SQLServer::Type::Timestamp.new
|
|
326
359
|
end
|
|
327
360
|
|
|
328
|
-
def translate_exception(e, message)
|
|
361
|
+
def translate_exception(e, message:, sql:, binds:)
|
|
329
362
|
case message
|
|
330
363
|
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
|
|
331
|
-
RecordNotUnique.new(message)
|
|
332
|
-
when /conflicted with the foreign key constraint/i
|
|
333
|
-
InvalidForeignKey.new(message)
|
|
364
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
|
365
|
+
when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i
|
|
366
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
|
334
367
|
when /has been chosen as the deadlock victim/i
|
|
335
|
-
DeadlockVictim.new(message)
|
|
368
|
+
DeadlockVictim.new(message, sql: sql, binds: binds)
|
|
336
369
|
when /database .* does not exist/i
|
|
337
|
-
NoDatabaseError.new(message)
|
|
370
|
+
NoDatabaseError.new(message, sql: sql, binds: binds)
|
|
338
371
|
when /data would be truncated/
|
|
339
|
-
ValueTooLong.new(message)
|
|
372
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
|
373
|
+
when /connection timed out/
|
|
374
|
+
StatementTimeout.new(message, sql: sql, binds: binds)
|
|
340
375
|
when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/
|
|
341
376
|
pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2)
|
|
342
377
|
MismatchedForeignKey.new(
|
|
@@ -348,9 +383,9 @@ module ActiveRecord
|
|
|
348
383
|
primary_key: pk_id.object
|
|
349
384
|
)
|
|
350
385
|
when /Cannot insert the value NULL into column.*does not allow nulls/
|
|
351
|
-
NotNullViolation.new(message)
|
|
386
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
|
352
387
|
when /Arithmetic overflow error/
|
|
353
|
-
RangeError.new(message)
|
|
388
|
+
RangeError.new(message, sql: sql, binds: binds)
|
|
354
389
|
else
|
|
355
390
|
super
|
|
356
391
|
end
|
|
@@ -364,7 +399,7 @@ module ActiveRecord
|
|
|
364
399
|
when :dblib
|
|
365
400
|
dblib_connect(config)
|
|
366
401
|
end
|
|
367
|
-
@spid = _raw_select(
|
|
402
|
+
@spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first
|
|
368
403
|
@version_year = version_year
|
|
369
404
|
configure_connection
|
|
370
405
|
end
|
|
@@ -383,32 +418,32 @@ module ActiveRecord
|
|
|
383
418
|
username: config[:username],
|
|
384
419
|
password: config[:password],
|
|
385
420
|
database: config[:database],
|
|
386
|
-
tds_version: config[:tds_version] ||
|
|
421
|
+
tds_version: config[:tds_version] || "7.3",
|
|
387
422
|
appname: config_appname(config),
|
|
388
423
|
login_timeout: config_login_timeout(config),
|
|
389
424
|
timeout: config_timeout(config),
|
|
390
|
-
encoding:
|
|
425
|
+
encoding: config_encoding(config),
|
|
391
426
|
azure: config[:azure],
|
|
392
427
|
contained: config[:contained]
|
|
393
428
|
).tap do |client|
|
|
394
429
|
if config[:azure]
|
|
395
|
-
client.execute(
|
|
396
|
-
client.execute(
|
|
397
|
-
client.execute(
|
|
398
|
-
client.execute(
|
|
430
|
+
client.execute("SET ANSI_NULLS ON").do
|
|
431
|
+
client.execute("SET ANSI_NULL_DFLT_ON ON").do
|
|
432
|
+
client.execute("SET ANSI_PADDING ON").do
|
|
433
|
+
client.execute("SET ANSI_WARNINGS ON").do
|
|
399
434
|
else
|
|
400
|
-
client.execute(
|
|
435
|
+
client.execute("SET ANSI_DEFAULTS ON").do
|
|
401
436
|
end
|
|
402
|
-
client.execute(
|
|
403
|
-
client.execute(
|
|
404
|
-
client.execute(
|
|
405
|
-
client.execute(
|
|
406
|
-
client.execute(
|
|
437
|
+
client.execute("SET QUOTED_IDENTIFIER ON").do
|
|
438
|
+
client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do
|
|
439
|
+
client.execute("SET IMPLICIT_TRANSACTIONS OFF").do
|
|
440
|
+
client.execute("SET TEXTSIZE 2147483647").do
|
|
441
|
+
client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do
|
|
407
442
|
end
|
|
408
443
|
end
|
|
409
444
|
|
|
410
445
|
def config_appname(config)
|
|
411
|
-
config[:appname] || configure_application_name || Rails.application.class.name.split(
|
|
446
|
+
config[:appname] || configure_application_name || Rails.application.class.name.split("::").first rescue nil
|
|
412
447
|
end
|
|
413
448
|
|
|
414
449
|
def config_login_timeout(config)
|
|
@@ -423,18 +458,18 @@ module ActiveRecord
|
|
|
423
458
|
config[:encoding].present? ? config[:encoding] : nil
|
|
424
459
|
end
|
|
425
460
|
|
|
426
|
-
def configure_connection
|
|
461
|
+
def configure_connection; end
|
|
427
462
|
|
|
428
|
-
def configure_application_name
|
|
463
|
+
def configure_application_name; end
|
|
429
464
|
|
|
430
465
|
def initialize_dateformatter
|
|
431
466
|
@database_dateformat = user_options_dateformat
|
|
432
467
|
a, b, c = @database_dateformat.each_char.to_a
|
|
433
|
-
[a, b, c].each { |f| f.upcase! if f ==
|
|
468
|
+
[a, b, c].each { |f| f.upcase! if f == "y" }
|
|
434
469
|
dateformat = "%#{a}-%#{b}-%#{c}"
|
|
435
470
|
::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
436
471
|
::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
437
|
-
::Time::DATE_FORMATS[:_sqlserver_time] =
|
|
472
|
+
::Time::DATE_FORMATS[:_sqlserver_time] = "%H:%M:%S"
|
|
438
473
|
::Time::DATE_FORMATS[:_sqlserver_datetime] = "#{dateformat} %H:%M:%S"
|
|
439
474
|
::Time::DATE_FORMATS[:_sqlserver_datetimeoffset] = lambda { |time|
|
|
440
475
|
time.strftime "#{dateformat} %H:%M:%S.%9N #{time.formatted_offset}"
|
|
@@ -443,13 +478,14 @@ module ActiveRecord
|
|
|
443
478
|
|
|
444
479
|
def version_year
|
|
445
480
|
return 2016 if sqlserver_version =~ /vNext/
|
|
481
|
+
|
|
446
482
|
/SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i
|
|
447
|
-
rescue StandardError
|
|
483
|
+
rescue StandardError
|
|
448
484
|
2016
|
|
449
485
|
end
|
|
450
486
|
|
|
451
487
|
def sqlserver_version
|
|
452
|
-
@sqlserver_version ||= _raw_select(
|
|
488
|
+
@sqlserver_version ||= _raw_select("SELECT @@version", fetch: :rows).first.first.to_s
|
|
453
489
|
end
|
|
454
490
|
end
|
|
455
491
|
end
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
class SQLServerColumn < Column
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
super(name, default, sql_type_metadata, null, table_name, default_function, collation, comment: comment)
|
|
6
|
+
def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **sqlserver_options)
|
|
7
|
+
@sqlserver_options = sqlserver_options
|
|
8
|
+
super
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def is_identity?
|
|
@@ -15,6 +16,10 @@ module ActiveRecord
|
|
|
15
16
|
@sqlserver_options[:is_primary]
|
|
16
17
|
end
|
|
17
18
|
|
|
19
|
+
def table_name
|
|
20
|
+
@sqlserver_options[:table_name]
|
|
21
|
+
end
|
|
22
|
+
|
|
18
23
|
def is_utf8?
|
|
19
24
|
sql_type =~ /nvarchar|ntext|nchar/i
|
|
20
25
|
end
|
|
@@ -22,7 +27,6 @@ module ActiveRecord
|
|
|
22
27
|
def case_sensitive?
|
|
23
28
|
collation && collation.match(/_CS/)
|
|
24
29
|
end
|
|
25
|
-
|
|
26
30
|
end
|
|
27
31
|
end
|
|
28
32
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionHandling
|
|
3
5
|
def sqlserver_connection(config) #:nodoc:
|
|
@@ -6,11 +8,17 @@ module ActiveRecord
|
|
|
6
8
|
mode = config[:mode].to_s.downcase.underscore.to_sym
|
|
7
9
|
case mode
|
|
8
10
|
when :dblib
|
|
9
|
-
require
|
|
11
|
+
require "tiny_tds"
|
|
10
12
|
else
|
|
11
13
|
raise ArgumentError, "Unknown connection mode in #{config.inspect}."
|
|
12
14
|
end
|
|
13
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
|
|
14
22
|
end
|
|
15
23
|
end
|
|
16
24
|
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_record/tasks/database_tasks"
|
|
4
|
+
require "shellwords"
|
|
5
|
+
require "ipaddr"
|
|
6
|
+
require "socket"
|
|
5
7
|
|
|
6
8
|
module ActiveRecord
|
|
7
9
|
module Tasks
|
|
8
|
-
|
|
9
10
|
class SQLServerDatabaseTasks
|
|
10
|
-
|
|
11
|
-
DEFAULT_COLLATION = 'SQL_Latin1_General_CP1_CI_AS'
|
|
11
|
+
DEFAULT_COLLATION = "SQL_Latin1_General_CP1_CI_AS"
|
|
12
12
|
|
|
13
13
|
delegate :connection, :establish_connection, :clear_active_connections!,
|
|
14
14
|
to: ActiveRecord::Base
|
|
@@ -19,10 +19,10 @@ module ActiveRecord
|
|
|
19
19
|
|
|
20
20
|
def create(master_established = false)
|
|
21
21
|
establish_master_connection unless master_established
|
|
22
|
-
connection.create_database configuration[
|
|
22
|
+
connection.create_database configuration["database"], configuration.merge("collation" => default_collation)
|
|
23
23
|
establish_connection configuration
|
|
24
|
-
rescue ActiveRecord::StatementInvalid =>
|
|
25
|
-
if /database .* already exists/i ===
|
|
24
|
+
rescue ActiveRecord::StatementInvalid => e
|
|
25
|
+
if /database .* already exists/i === e.message
|
|
26
26
|
raise DatabaseAlreadyExists
|
|
27
27
|
else
|
|
28
28
|
raise
|
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
|
31
31
|
|
|
32
32
|
def drop
|
|
33
33
|
establish_master_connection
|
|
34
|
-
connection.drop_database configuration[
|
|
34
|
+
connection.drop_database configuration["database"]
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def charset
|
|
@@ -50,7 +50,7 @@ module ActiveRecord
|
|
|
50
50
|
|
|
51
51
|
def structure_dump(filename, extra_flags)
|
|
52
52
|
server_arg = "-S #{Shellwords.escape(configuration['host'])}"
|
|
53
|
-
server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration[
|
|
53
|
+
server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration["port"]
|
|
54
54
|
command = [
|
|
55
55
|
"defncopy-ttds",
|
|
56
56
|
server_arg,
|
|
@@ -63,13 +63,14 @@ module ActiveRecord
|
|
|
63
63
|
command.concat(table_args)
|
|
64
64
|
view_args = connection.views.map { |v| Shellwords.escape(v) }
|
|
65
65
|
command.concat(view_args)
|
|
66
|
-
raise
|
|
66
|
+
raise "Error dumping database" unless Kernel.system(command.join(" "))
|
|
67
|
+
|
|
67
68
|
dump = File.read(filename)
|
|
68
|
-
dump.gsub!(/^USE .*$\nGO\n/,
|
|
69
|
-
dump.gsub!(/^GO\n/,
|
|
70
|
-
dump.gsub!(/nvarchar\(8000\)/,
|
|
71
|
-
dump.gsub!(/nvarchar\(-1\)/,
|
|
72
|
-
dump.gsub!(/text\(\d+\)/,
|
|
69
|
+
dump.gsub!(/^USE .*$\nGO\n/, "") # Strip db USE statements
|
|
70
|
+
dump.gsub!(/^GO\n/, "") # Strip db GO statements
|
|
71
|
+
dump.gsub!(/nvarchar\(8000\)/, "nvarchar(4000)") # Fix nvarchar(8000) column defs
|
|
72
|
+
dump.gsub!(/nvarchar\(-1\)/, "nvarchar(max)") # Fix nvarchar(-1) column defs
|
|
73
|
+
dump.gsub!(/text\(\d+\)/, "text") # Fix text(16) column defs
|
|
73
74
|
File.open(filename, "w") { |file| file.puts dump }
|
|
74
75
|
end
|
|
75
76
|
|
|
@@ -77,7 +78,6 @@ module ActiveRecord
|
|
|
77
78
|
connection.execute File.read(filename)
|
|
78
79
|
end
|
|
79
80
|
|
|
80
|
-
|
|
81
81
|
private
|
|
82
82
|
|
|
83
83
|
def configuration
|
|
@@ -85,25 +85,22 @@ module ActiveRecord
|
|
|
85
85
|
end
|
|
86
86
|
|
|
87
87
|
def default_collation
|
|
88
|
-
configuration[
|
|
88
|
+
configuration["collation"] || DEFAULT_COLLATION
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def establish_master_connection
|
|
92
|
-
establish_connection configuration.merge(
|
|
92
|
+
establish_connection configuration.merge("database" => "master")
|
|
93
93
|
end
|
|
94
|
-
|
|
95
94
|
end
|
|
96
95
|
|
|
97
96
|
module DatabaseTasksSQLServer
|
|
98
|
-
|
|
99
97
|
extend ActiveSupport::Concern
|
|
100
98
|
|
|
101
99
|
module ClassMethods
|
|
102
|
-
|
|
103
100
|
LOCAL_IPADDR = [
|
|
104
|
-
IPAddr.new(
|
|
105
|
-
IPAddr.new(
|
|
106
|
-
IPAddr.new(
|
|
101
|
+
IPAddr.new("192.168.0.0/16"),
|
|
102
|
+
IPAddr.new("10.0.0.0/8"),
|
|
103
|
+
IPAddr.new("172.16.0.0/12")
|
|
107
104
|
]
|
|
108
105
|
|
|
109
106
|
private
|
|
@@ -113,21 +110,20 @@ module ActiveRecord
|
|
|
113
110
|
end
|
|
114
111
|
|
|
115
112
|
def configuration_host_ip(configuration)
|
|
116
|
-
return nil unless configuration[
|
|
117
|
-
|
|
113
|
+
return nil unless configuration["host"]
|
|
114
|
+
|
|
115
|
+
Socket::getaddrinfo(configuration["host"], "echo", Socket::AF_INET)[0][3]
|
|
118
116
|
end
|
|
119
117
|
|
|
120
118
|
def local_ipaddr?(host_ip)
|
|
121
119
|
return false unless host_ip
|
|
120
|
+
|
|
122
121
|
LOCAL_IPADDR.any? { |ip| ip.include?(host_ip) }
|
|
123
122
|
end
|
|
124
|
-
|
|
125
123
|
end
|
|
126
|
-
|
|
127
124
|
end
|
|
128
125
|
|
|
129
126
|
DatabaseTasks.register_task %r{sqlserver}, SQLServerDatabaseTasks
|
|
130
127
|
DatabaseTasks.send :include, DatabaseTasksSQLServer
|
|
131
|
-
|
|
132
128
|
end
|
|
133
129
|
end
|