activerecord-sqlserver-adapter 8.0.10 → 8.1.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/.devcontainer/Dockerfile +1 -1
- data/.github/workflows/ci.yml +34 -3
- data/CHANGELOG.md +14 -68
- data/Dockerfile.ci +1 -1
- data/Gemfile +7 -9
- data/Guardfile +2 -2
- data/README.md +33 -13
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +15 -16
- data/compose.ci.yaml +8 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +1 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +118 -83
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +7 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +24 -12
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +17 -8
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +162 -156
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +5 -5
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +4 -6
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -12
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +118 -66
- data/lib/active_record/connection_adapters/sqlserver_column.rb +17 -9
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +5 -5
- data/lib/arel/visitors/sqlserver.rb +55 -26
- data/test/cases/active_schema_test_sqlserver.rb +45 -23
- data/test/cases/adapter_test_sqlserver.rb +72 -59
- data/test/cases/coerced_tests.rb +365 -161
- data/test/cases/column_test_sqlserver.rb +328 -316
- data/test/cases/connection_test_sqlserver.rb +15 -11
- data/test/cases/enum_test_sqlserver.rb +8 -9
- data/test/cases/execute_procedure_test_sqlserver.rb +1 -1
- data/test/cases/fetch_test_sqlserver.rb +1 -1
- data/test/cases/helper_sqlserver.rb +7 -3
- data/test/cases/index_test_sqlserver.rb +8 -6
- data/test/cases/insert_all_test_sqlserver.rb +3 -28
- data/test/cases/json_test_sqlserver.rb +8 -8
- data/test/cases/lateral_test_sqlserver.rb +2 -2
- data/test/cases/migration_test_sqlserver.rb +12 -12
- data/test/cases/pessimistic_locking_test_sqlserver.rb +6 -6
- data/test/cases/primary_keys_test_sqlserver.rb +4 -4
- data/test/cases/rake_test_sqlserver.rb +15 -7
- data/test/cases/schema_dumper_test_sqlserver.rb +109 -113
- data/test/cases/schema_test_sqlserver.rb +7 -7
- data/test/cases/transaction_test_sqlserver.rb +6 -8
- data/test/cases/trigger_test_sqlserver.rb +1 -1
- data/test/cases/utils_test_sqlserver.rb +3 -3
- data/test/cases/view_test_sqlserver.rb +12 -8
- data/test/cases/virtual_column_test_sqlserver.rb +113 -0
- data/test/migrations/create_clients_and_change_column_collation.rb +2 -2
- data/test/models/sqlserver/edge_schema.rb +2 -2
- data/test/schema/sqlserver_specific_schema.rb +49 -37
- data/test/support/coerceable_test_sqlserver.rb +10 -10
- data/test/support/connection_reflection.rb +0 -5
- data/test/support/core_ext/backtrace_cleaner.rb +36 -0
- data/test/support/query_assertions.rb +6 -6
- data/test/support/rake_helpers.rb +6 -10
- metadata +12 -107
|
@@ -11,13 +11,12 @@ module ActiveRecord
|
|
|
11
11
|
"datetime"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def serialize(
|
|
14
|
+
def serialize(_value)
|
|
15
15
|
value = super
|
|
16
16
|
return value unless value.acts_like?(:time)
|
|
17
17
|
|
|
18
18
|
datetime = "#{value.to_formatted_s(:_sqlserver_datetime)}.#{quote_fractional(value)}"
|
|
19
|
-
|
|
20
|
-
Data.new datetime, self
|
|
19
|
+
Data.new(datetime, self)
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
def deserialize(value)
|
|
@@ -37,7 +36,7 @@ module ActiveRecord
|
|
|
37
36
|
def fast_string_to_time(string)
|
|
38
37
|
time = ActiveSupport::TimeZone["UTC"].strptime(string, fast_string_to_time_format)
|
|
39
38
|
new_time(time.year, time.month, time.day, time.hour,
|
|
40
|
-
|
|
39
|
+
time.min, time.sec, Rational(time.nsec, 1_000))
|
|
41
40
|
rescue ArgumentError
|
|
42
41
|
super
|
|
43
42
|
end
|
|
@@ -7,13 +7,12 @@ module ActiveRecord
|
|
|
7
7
|
class Time < ActiveRecord::Type::Time
|
|
8
8
|
include TimeValueFractional2
|
|
9
9
|
|
|
10
|
-
def serialize(
|
|
10
|
+
def serialize(_value)
|
|
11
11
|
value = super
|
|
12
12
|
return value unless value.acts_like?(:time)
|
|
13
13
|
|
|
14
14
|
time = "#{value.to_formatted_s(:_sqlserver_time)}.#{quote_fractional(value)}"
|
|
15
|
-
|
|
16
|
-
Data.new time, self
|
|
15
|
+
Data.new(time, self)
|
|
17
16
|
end
|
|
18
17
|
|
|
19
18
|
def deserialize(value)
|
|
@@ -34,12 +33,11 @@ module ActiveRecord
|
|
|
34
33
|
|
|
35
34
|
private
|
|
36
35
|
|
|
37
|
-
def cast_value(
|
|
36
|
+
def cast_value(_value)
|
|
38
37
|
value = super
|
|
39
|
-
|
|
40
38
|
return value unless value.is_a?(::Time)
|
|
41
39
|
|
|
42
|
-
value = value.change(year: 2000, month:
|
|
40
|
+
value = value.change(year: 2000, month: 0o1, day: 0o1)
|
|
43
41
|
apply_seconds_precision(value)
|
|
44
42
|
end
|
|
45
43
|
|
|
@@ -10,8 +10,8 @@ module ActiveRecord
|
|
|
10
10
|
# Inspired from Rails PostgreSQL::Name adapter object in their own Utils.
|
|
11
11
|
class Name
|
|
12
12
|
UNQUOTED_SCANNER = /\]?\./
|
|
13
|
-
QUOTED_SCANNER
|
|
14
|
-
QUOTED_CHECKER
|
|
13
|
+
QUOTED_SCANNER = /\A\[.*?\]\./
|
|
14
|
+
QUOTED_CHECKER = /\A\[/
|
|
15
15
|
|
|
16
16
|
attr_reader :server, :database, :schema, :object
|
|
17
17
|
attr_reader :raw_name
|
|
@@ -38,7 +38,7 @@ module ActiveRecord
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def fully_qualified_database_quoted
|
|
41
|
-
[server_quoted, database_quoted].compact.join(
|
|
41
|
+
[server_quoted, database_quoted].compact.join(".")
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def fully_qualified?
|
|
@@ -65,7 +65,7 @@ module ActiveRecord
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def quoted
|
|
68
|
-
parts.map { |p| quote(p) if p }.join(
|
|
68
|
+
parts.map { |p| quote(p) if p }.join(".")
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def quoted_raw
|
|
@@ -107,32 +107,30 @@ module ActiveRecord
|
|
|
107
107
|
@schema = @parts.first
|
|
108
108
|
end
|
|
109
109
|
rest = scanner.rest
|
|
110
|
-
rest = rest.start_with?(".") ? rest[1
|
|
110
|
+
rest = rest.start_with?(".") ? rest[1..] : rest[0..]
|
|
111
111
|
@object = unquote(rest)
|
|
112
112
|
@parts << @object
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
def quote(part)
|
|
116
|
-
|
|
116
|
+
/\A\[.*\]\z/.match?(part) ? part : "[#{part.to_s.gsub("]", "]]")}]"
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def unquote(part)
|
|
120
|
-
if part
|
|
120
|
+
if part&.start_with?("[")
|
|
121
121
|
part[1..-2]
|
|
122
122
|
else
|
|
123
123
|
part
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
@parts
|
|
129
|
-
end
|
|
127
|
+
attr_reader :parts
|
|
130
128
|
end
|
|
131
129
|
|
|
132
130
|
extend self
|
|
133
131
|
|
|
134
132
|
def quote_string(s)
|
|
135
|
-
s.to_s.gsub(
|
|
133
|
+
s.to_s.gsub("'", "''")
|
|
136
134
|
end
|
|
137
135
|
|
|
138
136
|
def quote_string_single(s)
|
|
@@ -148,7 +146,7 @@ module ActiveRecord
|
|
|
148
146
|
end
|
|
149
147
|
|
|
150
148
|
def unquote_string(s)
|
|
151
|
-
s.to_s.gsub(
|
|
149
|
+
s.to_s.gsub("''", "'")
|
|
152
150
|
end
|
|
153
151
|
|
|
154
152
|
def extract_identifiers(name)
|
|
@@ -36,16 +36,16 @@ module ActiveRecord
|
|
|
36
36
|
register "sqlserver", "ActiveRecord::ConnectionAdapters::SQLServerAdapter", "active_record/connection_adapters/sqlserver_adapter"
|
|
37
37
|
|
|
38
38
|
class SQLServerAdapter < AbstractAdapter
|
|
39
|
-
include SQLServer::
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
include SQLServer::Savepoints
|
|
40
|
+
include SQLServer::DatabaseTasks
|
|
41
|
+
include SQLServer::DatabaseLimits
|
|
42
|
+
include SQLServer::SchemaStatements
|
|
43
|
+
include SQLServer::Showplan
|
|
44
|
+
include SQLServer::DatabaseStatements
|
|
45
|
+
include SQLServer::Quoting
|
|
46
|
+
include SQLServer::Version
|
|
47
47
|
|
|
48
|
-
ADAPTER_NAME = "SQLServer"
|
|
48
|
+
ADAPTER_NAME = "SQLServer"
|
|
49
49
|
|
|
50
50
|
# Default precision for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql)
|
|
51
51
|
DEFAULT_TIME_PRECISION = 7
|
|
@@ -62,18 +62,52 @@ module ActiveRecord
|
|
|
62
62
|
self.use_output_inserted = true
|
|
63
63
|
self.exclude_output_inserted_table_names = Concurrent::Map.new { false }
|
|
64
64
|
|
|
65
|
+
NATIVE_DATABASE_TYPES = {
|
|
66
|
+
primary_key: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY",
|
|
67
|
+
primary_key_nonclustered: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED",
|
|
68
|
+
integer: {name: "int", limit: 4},
|
|
69
|
+
bigint: {name: "bigint"},
|
|
70
|
+
boolean: {name: "bit"},
|
|
71
|
+
decimal: {name: "decimal"},
|
|
72
|
+
money: {name: "money"},
|
|
73
|
+
smallmoney: {name: "smallmoney"},
|
|
74
|
+
float: {name: "float"},
|
|
75
|
+
real: {name: "real"},
|
|
76
|
+
date: {name: "date"},
|
|
77
|
+
datetime: {name: "datetime"},
|
|
78
|
+
datetime2: {name: "datetime2"},
|
|
79
|
+
datetimeoffset: {name: "datetimeoffset"},
|
|
80
|
+
smalldatetime: {name: "smalldatetime"},
|
|
81
|
+
timestamp: {name: "datetime2(6)"},
|
|
82
|
+
time: {name: "time"},
|
|
83
|
+
char: {name: "char"},
|
|
84
|
+
varchar: {name: "varchar", limit: 8000},
|
|
85
|
+
varchar_max: {name: "varchar(max)"},
|
|
86
|
+
text_basic: {name: "text"},
|
|
87
|
+
nchar: {name: "nchar"},
|
|
88
|
+
string: {name: "nvarchar", limit: 4000},
|
|
89
|
+
text: {name: "nvarchar(max)"},
|
|
90
|
+
ntext: {name: "ntext"},
|
|
91
|
+
binary_basic: {name: "binary"},
|
|
92
|
+
varbinary: {name: "varbinary", limit: 8000},
|
|
93
|
+
binary: {name: "varbinary(max)"},
|
|
94
|
+
uuid: {name: "uniqueidentifier"},
|
|
95
|
+
ss_timestamp: {name: "timestamp"},
|
|
96
|
+
json: {name: "nvarchar(max)"}
|
|
97
|
+
}
|
|
98
|
+
|
|
65
99
|
class << self
|
|
66
100
|
def dbconsole(config, options = {})
|
|
67
101
|
sqlserver_config = config.configuration_hash
|
|
68
102
|
args = []
|
|
69
103
|
|
|
70
|
-
args += ["-d",
|
|
71
|
-
args += ["-U",
|
|
72
|
-
args += ["-P",
|
|
104
|
+
args += ["-d", config.database.to_s] if config.database
|
|
105
|
+
args += ["-U", sqlserver_config[:username].to_s] if sqlserver_config[:username]
|
|
106
|
+
args += ["-P", sqlserver_config[:password].to_s] if sqlserver_config[:password]
|
|
73
107
|
|
|
74
108
|
if sqlserver_config[:host]
|
|
75
|
-
host_arg =
|
|
76
|
-
host_arg
|
|
109
|
+
host_arg = "tcp:#{sqlserver_config[:host]}"
|
|
110
|
+
host_arg += ",#{sqlserver_config[:port]}" if sqlserver_config[:port]
|
|
77
111
|
args += ["-S", host_arg]
|
|
78
112
|
end
|
|
79
113
|
|
|
@@ -83,7 +117,7 @@ module ActiveRecord
|
|
|
83
117
|
def new_client(config)
|
|
84
118
|
TinyTds::Client.new(config)
|
|
85
119
|
rescue TinyTds::Error => error
|
|
86
|
-
if
|
|
120
|
+
if /database .* does not exist/i.match?(error.message)
|
|
87
121
|
raise ActiveRecord::NoDatabaseError
|
|
88
122
|
else
|
|
89
123
|
raise
|
|
@@ -95,6 +129,10 @@ module ActiveRecord
|
|
|
95
129
|
rescue
|
|
96
130
|
nil # Might not be in a Rails context so we fallback to `nil`.
|
|
97
131
|
end
|
|
132
|
+
|
|
133
|
+
def native_database_types # :nodoc:
|
|
134
|
+
NATIVE_DATABASE_TYPES
|
|
135
|
+
end
|
|
98
136
|
end
|
|
99
137
|
|
|
100
138
|
def initialize(...)
|
|
@@ -143,6 +181,10 @@ module ActiveRecord
|
|
|
143
181
|
true
|
|
144
182
|
end
|
|
145
183
|
|
|
184
|
+
def supports_index_include?
|
|
185
|
+
true
|
|
186
|
+
end
|
|
187
|
+
|
|
146
188
|
def supports_expression_index?
|
|
147
189
|
false
|
|
148
190
|
end
|
|
@@ -223,6 +265,10 @@ module ActiveRecord
|
|
|
223
265
|
false
|
|
224
266
|
end
|
|
225
267
|
|
|
268
|
+
def supports_virtual_columns?
|
|
269
|
+
true
|
|
270
|
+
end
|
|
271
|
+
|
|
226
272
|
def return_value_after_insert?(column) # :nodoc:
|
|
227
273
|
column.is_primary? || column.is_identity?
|
|
228
274
|
end
|
|
@@ -238,13 +284,20 @@ module ActiveRecord
|
|
|
238
284
|
# === Abstract Adapter (Connection Management) ================== #
|
|
239
285
|
|
|
240
286
|
def active?
|
|
241
|
-
@raw_connection&.active?
|
|
287
|
+
if @raw_connection&.active?
|
|
288
|
+
verified!
|
|
289
|
+
true
|
|
290
|
+
end
|
|
242
291
|
rescue *connection_errors
|
|
243
292
|
false
|
|
244
293
|
end
|
|
245
294
|
|
|
246
295
|
def reconnect
|
|
247
|
-
|
|
296
|
+
begin
|
|
297
|
+
@raw_connection&.close
|
|
298
|
+
rescue
|
|
299
|
+
nil
|
|
300
|
+
end
|
|
248
301
|
@raw_connection = nil
|
|
249
302
|
@spid = nil
|
|
250
303
|
@collation = nil
|
|
@@ -255,7 +308,11 @@ module ActiveRecord
|
|
|
255
308
|
def disconnect!
|
|
256
309
|
super
|
|
257
310
|
|
|
258
|
-
|
|
311
|
+
begin
|
|
312
|
+
@raw_connection&.close
|
|
313
|
+
rescue
|
|
314
|
+
nil
|
|
315
|
+
end
|
|
259
316
|
@raw_connection = nil
|
|
260
317
|
@spid = nil
|
|
261
318
|
@collation = nil
|
|
@@ -324,13 +381,6 @@ module ActiveRecord
|
|
|
324
381
|
self.class::VERSION
|
|
325
382
|
end
|
|
326
383
|
|
|
327
|
-
def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], having_clause: [], limit: nil, offset: nil)
|
|
328
|
-
result = from_clause + join_clause + where_clause + having_clause
|
|
329
|
-
result << offset if offset
|
|
330
|
-
result << limit if limit
|
|
331
|
-
result
|
|
332
|
-
end
|
|
333
|
-
|
|
334
384
|
def get_database_version # :nodoc:
|
|
335
385
|
version_year
|
|
336
386
|
end
|
|
@@ -345,21 +395,21 @@ module ActiveRecord
|
|
|
345
395
|
protected
|
|
346
396
|
|
|
347
397
|
def initialize_type_map(m)
|
|
348
|
-
m.register_type
|
|
398
|
+
m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
|
|
349
399
|
|
|
350
400
|
# Exact Numerics
|
|
351
|
-
register_class_with_limit m, "bigint(8)",
|
|
352
|
-
m.alias_type
|
|
353
|
-
register_class_with_limit m, "int(4)",
|
|
354
|
-
m.alias_type
|
|
355
|
-
m.alias_type
|
|
356
|
-
register_class_with_limit m, "smallint(2)",
|
|
357
|
-
m.alias_type
|
|
358
|
-
register_class_with_limit m, "tinyint(1)",
|
|
359
|
-
m.alias_type
|
|
360
|
-
m.register_type
|
|
361
|
-
m.register_type
|
|
362
|
-
scale
|
|
401
|
+
register_class_with_limit m, "bigint(8)", SQLServer::Type::BigInteger
|
|
402
|
+
m.alias_type "bigint", "bigint(8)"
|
|
403
|
+
register_class_with_limit m, "int(4)", SQLServer::Type::Integer
|
|
404
|
+
m.alias_type "integer", "int(4)"
|
|
405
|
+
m.alias_type "int", "int(4)"
|
|
406
|
+
register_class_with_limit m, "smallint(2)", SQLServer::Type::SmallInteger
|
|
407
|
+
m.alias_type "smallint", "smallint(2)"
|
|
408
|
+
register_class_with_limit m, "tinyint(1)", SQLServer::Type::TinyInteger
|
|
409
|
+
m.alias_type "tinyint", "tinyint(1)"
|
|
410
|
+
m.register_type "bit", SQLServer::Type::Boolean.new
|
|
411
|
+
m.register_type %r{\Adecimal}i do |sql_type|
|
|
412
|
+
scale = extract_scale(sql_type)
|
|
363
413
|
precision = extract_precision(sql_type)
|
|
364
414
|
if scale == 0
|
|
365
415
|
SQLServer::Type::DecimalWithoutScale.new(precision: precision)
|
|
@@ -367,17 +417,17 @@ module ActiveRecord
|
|
|
367
417
|
SQLServer::Type::Decimal.new(precision: precision, scale: scale)
|
|
368
418
|
end
|
|
369
419
|
end
|
|
370
|
-
m.alias_type
|
|
371
|
-
m.register_type
|
|
372
|
-
m.register_type
|
|
420
|
+
m.alias_type %r{\Anumeric}i, "decimal"
|
|
421
|
+
m.register_type "money", SQLServer::Type::Money.new
|
|
422
|
+
m.register_type "smallmoney", SQLServer::Type::SmallMoney.new
|
|
373
423
|
|
|
374
424
|
# Approximate Numerics
|
|
375
|
-
m.register_type
|
|
376
|
-
m.register_type
|
|
425
|
+
m.register_type "float", SQLServer::Type::Float.new
|
|
426
|
+
m.register_type "real", SQLServer::Type::Real.new
|
|
377
427
|
|
|
378
428
|
# Date and Time
|
|
379
|
-
m.register_type
|
|
380
|
-
m.register_type
|
|
429
|
+
m.register_type "date", SQLServer::Type::Date.new
|
|
430
|
+
m.register_type %r{\Adatetime} do |sql_type|
|
|
381
431
|
precision = extract_precision(sql_type)
|
|
382
432
|
if precision
|
|
383
433
|
SQLServer::Type::DateTime2.new precision: precision
|
|
@@ -389,34 +439,34 @@ module ActiveRecord
|
|
|
389
439
|
precision = extract_precision(sql_type)
|
|
390
440
|
SQLServer::Type::DateTimeOffset.new precision: precision
|
|
391
441
|
end
|
|
392
|
-
m.register_type
|
|
393
|
-
m.register_type
|
|
442
|
+
m.register_type "smalldatetime", SQLServer::Type::SmallDateTime.new
|
|
443
|
+
m.register_type %r{\Atime}i do |sql_type|
|
|
394
444
|
precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION
|
|
395
445
|
SQLServer::Type::Time.new precision: precision
|
|
396
446
|
end
|
|
397
447
|
|
|
398
448
|
# Character Strings
|
|
399
|
-
register_class_with_limit m, %r{\Achar}i,
|
|
400
|
-
register_class_with_limit m, %r{\Avarchar}i,
|
|
401
|
-
m.register_type
|
|
402
|
-
m.register_type
|
|
449
|
+
register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char
|
|
450
|
+
register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar
|
|
451
|
+
m.register_type "varchar(max)", SQLServer::Type::VarcharMax.new
|
|
452
|
+
m.register_type "text", SQLServer::Type::Text.new
|
|
403
453
|
|
|
404
454
|
# Unicode Character Strings
|
|
405
|
-
register_class_with_limit m, %r{\Anchar}i,
|
|
406
|
-
register_class_with_limit m, %r{\Anvarchar}i,
|
|
407
|
-
m.alias_type
|
|
408
|
-
m.register_type
|
|
409
|
-
m.register_type
|
|
410
|
-
m.register_type
|
|
455
|
+
register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar
|
|
456
|
+
register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
|
|
457
|
+
m.alias_type "string", "nvarchar(4000)"
|
|
458
|
+
m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
|
|
459
|
+
m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
|
|
460
|
+
m.register_type "ntext", SQLServer::Type::UnicodeText.new
|
|
411
461
|
|
|
412
462
|
# Binary Strings
|
|
413
|
-
register_class_with_limit m, %r{\Abinary}i,
|
|
414
|
-
register_class_with_limit m, %r{\Avarbinary}i,
|
|
415
|
-
m.register_type
|
|
463
|
+
register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
|
|
464
|
+
register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary
|
|
465
|
+
m.register_type "varbinary(max)", SQLServer::Type::VarbinaryMax.new
|
|
416
466
|
|
|
417
467
|
# Other Data Types
|
|
418
|
-
m.register_type
|
|
419
|
-
m.register_type
|
|
468
|
+
m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new
|
|
469
|
+
m.register_type "timestamp", SQLServer::Type::Timestamp.new
|
|
420
470
|
end
|
|
421
471
|
end
|
|
422
472
|
|
|
@@ -452,6 +502,8 @@ module ActiveRecord
|
|
|
452
502
|
NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
453
503
|
when /Arithmetic overflow error/
|
|
454
504
|
RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
505
|
+
when /statement conflicted with the CHECK constraint/
|
|
506
|
+
CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
455
507
|
else
|
|
456
508
|
super
|
|
457
509
|
end
|
|
@@ -471,10 +523,10 @@ module ActiveRecord
|
|
|
471
523
|
|
|
472
524
|
[a, b, c].each { |f| f.upcase! if f == "y" }
|
|
473
525
|
dateformat = "%#{a}-%#{b}-%#{c}"
|
|
474
|
-
::Date::DATE_FORMATS[:_sqlserver_dateformat]
|
|
475
|
-
::Time::DATE_FORMATS[:_sqlserver_dateformat]
|
|
476
|
-
::Time::DATE_FORMATS[:_sqlserver_time]
|
|
477
|
-
::Time::DATE_FORMATS[:_sqlserver_datetime]
|
|
526
|
+
::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
527
|
+
::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
528
|
+
::Time::DATE_FORMATS[:_sqlserver_time] = "%H:%M:%S"
|
|
529
|
+
::Time::DATE_FORMATS[:_sqlserver_datetime] = "#{dateformat} %H:%M:%S"
|
|
478
530
|
::Time::DATE_FORMATS[:_sqlserver_datetimeoffset] = lambda { |time|
|
|
479
531
|
time.strftime "#{dateformat} %H:%M:%S.%9N #{time.formatted_offset}"
|
|
480
532
|
}
|
|
@@ -6,12 +6,13 @@ module ActiveRecord
|
|
|
6
6
|
class Column < ConnectionAdapters::Column
|
|
7
7
|
delegate :is_identity, :is_primary, :table_name, :ordinal_position, to: :sql_type_metadata
|
|
8
8
|
|
|
9
|
-
def initialize(*, is_identity: nil, is_primary: nil, table_name: nil, ordinal_position: nil, **)
|
|
9
|
+
def initialize(*, is_identity: nil, is_primary: nil, table_name: nil, ordinal_position: nil, generated_type: nil, **)
|
|
10
10
|
super
|
|
11
11
|
@is_identity = is_identity
|
|
12
12
|
@is_primary = is_primary
|
|
13
13
|
@table_name = table_name
|
|
14
14
|
@ordinal_position = ordinal_position
|
|
15
|
+
@generated_type = generated_type
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def is_identity?
|
|
@@ -28,7 +29,19 @@ module ActiveRecord
|
|
|
28
29
|
end
|
|
29
30
|
|
|
30
31
|
def case_sensitive?
|
|
31
|
-
collation
|
|
32
|
+
collation&.match(/_CS/)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def virtual?
|
|
36
|
+
@generated_type.present?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def virtual_stored?
|
|
40
|
+
@generated_type == :stored
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def has_default?
|
|
44
|
+
super && !virtual?
|
|
32
45
|
end
|
|
33
46
|
|
|
34
47
|
def init_with(coder)
|
|
@@ -55,15 +68,10 @@ module ActiveRecord
|
|
|
55
68
|
table_name == other.table_name &&
|
|
56
69
|
ordinal_position == other.ordinal_position
|
|
57
70
|
end
|
|
58
|
-
|
|
71
|
+
alias_method :eql?, :==
|
|
59
72
|
|
|
60
73
|
def hash
|
|
61
|
-
Column.hash
|
|
62
|
-
super.hash ^
|
|
63
|
-
is_identity?.hash ^
|
|
64
|
-
is_primary?.hash ^
|
|
65
|
-
table_name.hash ^
|
|
66
|
-
ordinal_position.hash
|
|
74
|
+
[Column, super, is_identity?, is_primary?, table_name, ordinal_position].hash
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
private
|
|
@@ -28,7 +28,7 @@ module ActiveRecord
|
|
|
28
28
|
end
|
|
29
29
|
establish_connection(configuration)
|
|
30
30
|
rescue ActiveRecord::StatementInvalid => e
|
|
31
|
-
if /database .* already exists/i
|
|
31
|
+
if /database .* already exists/i.match?(e.message)
|
|
32
32
|
raise DatabaseAlreadyExists
|
|
33
33
|
else
|
|
34
34
|
raise
|
|
@@ -68,7 +68,7 @@ module ActiveRecord
|
|
|
68
68
|
"-D #{Shellwords.escape(configuration_hash[:database])}",
|
|
69
69
|
"-U #{Shellwords.escape(configuration_hash[:username])}",
|
|
70
70
|
"-P #{Shellwords.escape(configuration_hash[:password])}",
|
|
71
|
-
"-o #{Shellwords.escape(filename)}"
|
|
71
|
+
"-o #{Shellwords.escape(filename)}"
|
|
72
72
|
]
|
|
73
73
|
table_args = connection.tables.map { |t| Shellwords.escape(t) }
|
|
74
74
|
command.concat(table_args)
|
|
@@ -79,8 +79,8 @@ module ActiveRecord
|
|
|
79
79
|
dump = File.read(filename)
|
|
80
80
|
dump.gsub!(/^USE .*$\nGO\n/, "") # Strip db USE statements
|
|
81
81
|
dump.gsub!(/^GO\n/, "") # Strip db GO statements
|
|
82
|
-
dump.gsub!(
|
|
83
|
-
dump.gsub!(
|
|
82
|
+
dump.gsub!("nvarchar(8000)", "nvarchar(4000)") # Fix nvarchar(8000) column defs
|
|
83
|
+
dump.gsub!("nvarchar(-1)", "nvarchar(max)") # Fix nvarchar(-1) column defs
|
|
84
84
|
dump.gsub!(/text\(\d+\)/, "text") # Fix text(16) column defs
|
|
85
85
|
File.open(filename, "w") { |file| file.puts dump }
|
|
86
86
|
end
|
|
@@ -124,7 +124,7 @@ module ActiveRecord
|
|
|
124
124
|
def configuration_host_ip(configuration)
|
|
125
125
|
return nil unless configuration.host
|
|
126
126
|
|
|
127
|
-
Socket
|
|
127
|
+
Socket.getaddrinfo(configuration.host, "echo", Socket::AF_INET)[0][3]
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
def local_ipaddr?(host_ip)
|