activerecord-sqlserver-adapter 6.0.0.rc2 → 6.1.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/.github/workflows/ci.yml +26 -0
- data/CHANGELOG.md +20 -41
- data/README.md +32 -3
- data/RUNNING_UNIT_TESTS.md +1 -1
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +0 -9
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +7 -2
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +28 -16
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +7 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -1
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -3
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +31 -8
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +27 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +0 -1
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type.rb +1 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +100 -68
- data/lib/active_record/connection_adapters/sqlserver_column.rb +17 -0
- data/lib/active_record/sqlserver_base.rb +9 -15
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +17 -14
- data/lib/arel/visitors/sqlserver.rb +111 -39
- data/test/cases/adapter_test_sqlserver.rb +48 -14
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/coerced_tests.rb +598 -78
- data/test/cases/column_test_sqlserver.rb +5 -2
- data/test/cases/disconnected_test_sqlserver.rb +39 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +9 -0
- data/test/cases/in_clause_test_sqlserver.rb +27 -0
- data/test/cases/lateral_test_sqlserver.rb +35 -0
- data/test/cases/migration_test_sqlserver.rb +51 -0
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +7 -0
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +3 -2
- data/test/cases/schema_dumper_test_sqlserver.rb +20 -3
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/schema/sqlserver_specific_schema.rb +17 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump +0 -0
- data/test/support/sql_counter_sqlserver.rb +14 -12
- metadata +32 -13
- data/.travis.yml +0 -23
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
private
|
16
16
|
|
17
17
|
def explicit_primary_key_default?(column)
|
18
|
-
column.
|
18
|
+
column.type == :integer && !column.is_identity?
|
19
19
|
end
|
20
20
|
|
21
21
|
def schema_limit(column)
|
@@ -27,11 +27,17 @@ module ActiveRecord
|
|
27
27
|
def schema_collation(column)
|
28
28
|
return unless column.collation
|
29
29
|
|
30
|
-
|
30
|
+
# use inspect to ensure collation is dumped as string. Without this it's dumped as
|
31
|
+
# a constant ('collation: SQL_Latin1_General_CP1_CI_AS')
|
32
|
+
collation = column.collation.inspect
|
33
|
+
# use inspect to ensure string comparison
|
34
|
+
default_collation = @connection.collation.inspect
|
35
|
+
|
36
|
+
collation if collation != default_collation
|
31
37
|
end
|
32
38
|
|
33
39
|
def default_primary_key?(column)
|
34
|
-
super && column.
|
40
|
+
super && column.is_identity?
|
35
41
|
end
|
36
42
|
end
|
37
43
|
end
|
@@ -27,7 +27,7 @@ module ActiveRecord
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
if options[:if_exists] && @version_year < 2016
|
30
|
-
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}"
|
30
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}", "SCHEMA"
|
31
31
|
else
|
32
32
|
super
|
33
33
|
end
|
@@ -51,7 +51,7 @@ module ActiveRecord
|
|
51
51
|
index[:index_keys].split(",").each do |column|
|
52
52
|
column.strip!
|
53
53
|
|
54
|
-
if column.
|
54
|
+
if column.end_with?("(-)")
|
55
55
|
column.gsub! "(-)", ""
|
56
56
|
orders[column] = :desc
|
57
57
|
end
|
@@ -130,8 +130,9 @@ module ActiveRecord
|
|
130
130
|
rename_table_indexes(table_name, new_name)
|
131
131
|
end
|
132
132
|
|
133
|
-
def remove_column(table_name, column_name, type = nil, options
|
133
|
+
def remove_column(table_name, column_name, type = nil, **options)
|
134
134
|
raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_name.is_a? Array
|
135
|
+
return if options[:if_exists] == true && !column_exists?(table_name, column_name)
|
135
136
|
|
136
137
|
remove_check_constraints(table_name, column_name)
|
137
138
|
remove_default_constraint(table_name, column_name)
|
@@ -156,6 +157,7 @@ module ActiveRecord
|
|
156
157
|
end
|
157
158
|
sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil?
|
158
159
|
alter_command = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
|
160
|
+
alter_command += " COLLATE #{options[:collation]}" if options[:collation].present?
|
159
161
|
alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false
|
160
162
|
sql_commands << alter_command
|
161
163
|
if without_constraints
|
@@ -190,7 +192,7 @@ module ActiveRecord
|
|
190
192
|
end
|
191
193
|
|
192
194
|
def rename_index(table_name, old_name, new_name)
|
193
|
-
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{
|
195
|
+
raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters" if new_name.length > index_name_length
|
194
196
|
|
195
197
|
identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}")
|
196
198
|
execute_procedure :sp_rename, identifier.quoted, new_name, "INDEX"
|
@@ -281,14 +283,33 @@ module ActiveRecord
|
|
281
283
|
SQLServer::SchemaDumper.create(self, options)
|
282
284
|
end
|
283
285
|
|
286
|
+
def create_schema(schema_name, authorization = nil)
|
287
|
+
sql = "CREATE SCHEMA [#{schema_name}]"
|
288
|
+
sql += " AUTHORIZATION [#{authorization}]" if authorization
|
289
|
+
|
290
|
+
execute sql
|
291
|
+
end
|
292
|
+
|
293
|
+
def change_table_schema(schema_name, table_name)
|
294
|
+
execute "ALTER SCHEMA [#{schema_name}] TRANSFER [#{table_name}]"
|
295
|
+
end
|
296
|
+
|
297
|
+
def drop_schema(schema_name)
|
298
|
+
execute "DROP SCHEMA [#{schema_name}]"
|
299
|
+
end
|
300
|
+
|
284
301
|
private
|
285
302
|
|
286
303
|
def data_source_sql(name = nil, type: nil)
|
287
304
|
scope = quoted_scope name, type: type
|
288
|
-
|
305
|
+
|
306
|
+
table_name = lowercase_schema_reflection_sql 'TABLE_NAME'
|
307
|
+
database = scope[:database].present? ? "#{scope[:database]}." : ""
|
308
|
+
table_catalog = scope[:database].present? ? quote(scope[:database]) : "DB_NAME()"
|
309
|
+
|
289
310
|
sql = "SELECT #{table_name}"
|
290
|
-
sql += " FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)"
|
291
|
-
sql += " WHERE TABLE_CATALOG =
|
311
|
+
sql += " FROM #{database}INFORMATION_SCHEMA.TABLES WITH (NOLOCK)"
|
312
|
+
sql += " WHERE TABLE_CATALOG = #{table_catalog}"
|
292
313
|
sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
|
293
314
|
sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
|
294
315
|
sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
|
@@ -299,6 +320,7 @@ module ActiveRecord
|
|
299
320
|
def quoted_scope(name = nil, type: nil)
|
300
321
|
identifier = SQLServer::Utils.extract_identifiers(name)
|
301
322
|
{}.tap do |scope|
|
323
|
+
scope[:database] = identifier.database if identifier.database
|
302
324
|
scope[:schema] = identifier.schema || "dbo"
|
303
325
|
scope[:name] = identifier.object if identifier.object
|
304
326
|
scope[:type] = type if type
|
@@ -310,7 +332,7 @@ module ActiveRecord
|
|
310
332
|
def initialize_native_database_types
|
311
333
|
{
|
312
334
|
primary_key: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY",
|
313
|
-
primary_key_nonclustered: "
|
335
|
+
primary_key_nonclustered: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED",
|
314
336
|
integer: { name: "int", limit: 4 },
|
315
337
|
bigint: { name: "bigint" },
|
316
338
|
boolean: { name: "bit" },
|
@@ -472,6 +494,7 @@ module ActiveRecord
|
|
472
494
|
AND c.default_object_id = d.object_id
|
473
495
|
LEFT OUTER JOIN #{database}.sys.key_constraints k
|
474
496
|
ON c.object_id = k.parent_object_id
|
497
|
+
AND k.type = 'PK'
|
475
498
|
LEFT OUTER JOIN #{database}.sys.index_columns ic
|
476
499
|
ON k.parent_object_id = ic.object_id
|
477
500
|
AND k.unique_index_id = ic.index_id
|
@@ -3,16 +3,36 @@
|
|
3
3
|
module ActiveRecord
|
4
4
|
module ConnectionAdapters
|
5
5
|
module SQLServer
|
6
|
-
class
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
class TypeMetadata < DelegateClass(SqlTypeMetadata)
|
7
|
+
undef to_yaml if method_defined?(:to_yaml)
|
8
|
+
|
9
|
+
include Deduplicable
|
10
|
+
|
11
|
+
attr_reader :sqlserver_options
|
12
|
+
|
13
|
+
def initialize(type_metadata, sqlserver_options: nil)
|
14
|
+
super(type_metadata)
|
15
|
+
@sqlserver_options = sqlserver_options
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(other)
|
19
|
+
other.is_a?(TypeMetadata) &&
|
20
|
+
__getobj__ == other.__getobj__ &&
|
21
|
+
sqlserver_options == other.sqlserver_options
|
22
|
+
end
|
23
|
+
alias eql? ==
|
24
|
+
|
25
|
+
def hash
|
26
|
+
TypeMetadata.hash ^
|
27
|
+
__getobj__.hash ^
|
28
|
+
sqlserver_options.hash
|
10
29
|
end
|
11
30
|
|
12
|
-
|
31
|
+
private
|
13
32
|
|
14
|
-
def
|
15
|
-
|
33
|
+
def deduplicated
|
34
|
+
__setobj__(__getobj__.deduplicate)
|
35
|
+
super
|
16
36
|
end
|
17
37
|
end
|
18
38
|
end
|
@@ -31,9 +31,9 @@ module ActiveRecord
|
|
31
31
|
module SQLServerRealTransaction
|
32
32
|
attr_reader :starting_isolation_level
|
33
33
|
|
34
|
-
def initialize(connection,
|
34
|
+
def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false)
|
35
35
|
@connection = connection
|
36
|
-
@starting_isolation_level = current_isolation_level if
|
36
|
+
@starting_isolation_level = current_isolation_level if isolation
|
37
37
|
super
|
38
38
|
end
|
39
39
|
|
@@ -11,6 +11,7 @@ require "active_record/connection_adapters/sqlserver/type/small_integer"
|
|
11
11
|
require "active_record/connection_adapters/sqlserver/type/tiny_integer"
|
12
12
|
require "active_record/connection_adapters/sqlserver/type/boolean"
|
13
13
|
require "active_record/connection_adapters/sqlserver/type/decimal"
|
14
|
+
require "active_record/connection_adapters/sqlserver/type/decimal_without_scale"
|
14
15
|
require "active_record/connection_adapters/sqlserver/type/money"
|
15
16
|
require "active_record/connection_adapters/sqlserver/type/small_money"
|
16
17
|
# Approximate Numerics
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module SQLServer
|
6
|
+
module Type
|
7
|
+
class DecimalWithoutScale < ActiveRecord::Type::DecimalWithoutScale
|
8
|
+
def sqlserver_type
|
9
|
+
"decimal".yield_self do |type|
|
10
|
+
type += "(#{precision.to_i},0)" if precision
|
11
|
+
type
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def type_cast_for_schema(value)
|
16
|
+
value.is_a?(BigDecimal) ? value.to_s : value.inspect
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
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
|
-
|
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
|
-
|
67
|
-
initialize_dateformatter
|
68
|
-
use_database
|
128
|
+
configure_connection
|
69
129
|
end
|
70
130
|
|
71
131
|
# === Abstract Adapter ========================================== #
|
@@ -152,6 +212,10 @@ module ActiveRecord
|
|
152
212
|
true
|
153
213
|
end
|
154
214
|
|
215
|
+
def supports_optimizer_hints?
|
216
|
+
true
|
217
|
+
end
|
218
|
+
|
155
219
|
def supports_lazy_transactions?
|
156
220
|
true
|
157
221
|
end
|
@@ -222,6 +286,14 @@ module ActiveRecord
|
|
222
286
|
do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"
|
223
287
|
end
|
224
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
|
+
|
225
297
|
# === Abstract Adapter (Misc Support) =========================== #
|
226
298
|
|
227
299
|
def tables_with_referential_integrity
|
@@ -296,6 +368,7 @@ module ActiveRecord
|
|
296
368
|
|
297
369
|
def initialize_type_map(m = type_map)
|
298
370
|
m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
|
371
|
+
|
299
372
|
# Exact Numerics
|
300
373
|
register_class_with_limit m, "bigint(8)", SQLServer::Type::BigInteger
|
301
374
|
m.alias_type "bigint", "bigint(8)"
|
@@ -308,16 +381,22 @@ module ActiveRecord
|
|
308
381
|
m.alias_type "tinyint", "tinyint(1)"
|
309
382
|
m.register_type "bit", SQLServer::Type::Boolean.new
|
310
383
|
m.register_type %r{\Adecimal}i do |sql_type|
|
311
|
-
scale
|
384
|
+
scale = extract_scale(sql_type)
|
312
385
|
precision = extract_precision(sql_type)
|
313
|
-
|
386
|
+
if scale == 0
|
387
|
+
SQLServer::Type::DecimalWithoutScale.new(precision: precision)
|
388
|
+
else
|
389
|
+
SQLServer::Type::Decimal.new(precision: precision, scale: scale)
|
390
|
+
end
|
314
391
|
end
|
315
392
|
m.alias_type %r{\Anumeric}i, "decimal"
|
316
393
|
m.register_type "money", SQLServer::Type::Money.new
|
317
394
|
m.register_type "smallmoney", SQLServer::Type::SmallMoney.new
|
395
|
+
|
318
396
|
# Approximate Numerics
|
319
397
|
m.register_type "float", SQLServer::Type::Float.new
|
320
398
|
m.register_type "real", SQLServer::Type::Real.new
|
399
|
+
|
321
400
|
# Date and Time
|
322
401
|
m.register_type "date", SQLServer::Type::Date.new
|
323
402
|
m.register_type %r{\Adatetime} do |sql_type|
|
@@ -337,11 +416,13 @@ module ActiveRecord
|
|
337
416
|
precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION
|
338
417
|
SQLServer::Type::Time.new precision: precision
|
339
418
|
end
|
419
|
+
|
340
420
|
# Character Strings
|
341
421
|
register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char
|
342
422
|
register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar
|
343
423
|
m.register_type "varchar(max)", SQLServer::Type::VarcharMax.new
|
344
424
|
m.register_type "text", SQLServer::Type::Text.new
|
425
|
+
|
345
426
|
# Unicode Character Strings
|
346
427
|
register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar
|
347
428
|
register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
|
@@ -349,10 +430,12 @@ module ActiveRecord
|
|
349
430
|
m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
|
350
431
|
m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
|
351
432
|
m.register_type "ntext", SQLServer::Type::UnicodeText.new
|
433
|
+
|
352
434
|
# Binary Strings
|
353
435
|
register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
|
354
436
|
register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary
|
355
437
|
m.register_type "varbinary(max)", SQLServer::Type::VarbinaryMax.new
|
438
|
+
|
356
439
|
# Other Data Types
|
357
440
|
m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new
|
358
441
|
m.register_type "timestamp", SQLServer::Type::Timestamp.new
|
@@ -360,6 +443,8 @@ module ActiveRecord
|
|
360
443
|
|
361
444
|
def translate_exception(e, message:, sql:, binds:)
|
362
445
|
case message
|
446
|
+
when /(SQL Server client is not connected)|(failed to execute statement)/i
|
447
|
+
ConnectionNotEstablished.new(message)
|
363
448
|
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
|
364
449
|
RecordNotUnique.new(message, sql: sql, binds: binds)
|
365
450
|
when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i
|
@@ -393,78 +478,18 @@ module ActiveRecord
|
|
393
478
|
|
394
479
|
# === SQLServer Specific (Connection Management) ================ #
|
395
480
|
|
396
|
-
def connect
|
397
|
-
config = @connection_options
|
398
|
-
@connection = case config[:mode]
|
399
|
-
when :dblib
|
400
|
-
dblib_connect(config)
|
401
|
-
end
|
402
|
-
@spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first
|
403
|
-
@version_year = version_year
|
404
|
-
configure_connection
|
405
|
-
end
|
406
|
-
|
407
481
|
def connection_errors
|
408
482
|
@connection_errors ||= [].tap do |errors|
|
409
483
|
errors << TinyTds::Error if defined?(TinyTds::Error)
|
410
484
|
end
|
411
485
|
end
|
412
486
|
|
413
|
-
def dblib_connect(config)
|
414
|
-
TinyTds::Client.new(
|
415
|
-
dataserver: config[:dataserver],
|
416
|
-
host: config[:host],
|
417
|
-
port: config[:port],
|
418
|
-
username: config[:username],
|
419
|
-
password: config[:password],
|
420
|
-
database: config[:database],
|
421
|
-
tds_version: config[:tds_version] || "7.3",
|
422
|
-
appname: config_appname(config),
|
423
|
-
login_timeout: config_login_timeout(config),
|
424
|
-
timeout: config_timeout(config),
|
425
|
-
encoding: config_encoding(config),
|
426
|
-
azure: config[:azure],
|
427
|
-
contained: config[:contained]
|
428
|
-
).tap do |client|
|
429
|
-
if config[:azure]
|
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
|
434
|
-
else
|
435
|
-
client.execute("SET ANSI_DEFAULTS ON").do
|
436
|
-
end
|
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
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
def config_appname(config)
|
446
|
-
config[:appname] || configure_application_name || Rails.application.class.name.split("::").first rescue nil
|
447
|
-
end
|
448
|
-
|
449
|
-
def config_login_timeout(config)
|
450
|
-
config[:login_timeout].present? ? config[:login_timeout].to_i : nil
|
451
|
-
end
|
452
|
-
|
453
|
-
def config_timeout(config)
|
454
|
-
config[:timeout].present? ? config[:timeout].to_i / 1000 : nil
|
455
|
-
end
|
456
|
-
|
457
|
-
def config_encoding(config)
|
458
|
-
config[:encoding].present? ? config[:encoding] : nil
|
459
|
-
end
|
460
|
-
|
461
|
-
def configure_connection; end
|
462
|
-
|
463
487
|
def configure_application_name; end
|
464
488
|
|
465
489
|
def initialize_dateformatter
|
466
490
|
@database_dateformat = user_options_dateformat
|
467
491
|
a, b, c = @database_dateformat.each_char.to_a
|
492
|
+
|
468
493
|
[a, b, c].each { |f| f.upcase! if f == "y" }
|
469
494
|
dateformat = "%#{a}-%#{b}-%#{c}"
|
470
495
|
::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
@@ -487,6 +512,13 @@ module ActiveRecord
|
|
487
512
|
def sqlserver_version
|
488
513
|
@sqlserver_version ||= _raw_select("SELECT @@version", fetch: :rows).first.first.to_s
|
489
514
|
end
|
515
|
+
|
516
|
+
private
|
517
|
+
|
518
|
+
def connect
|
519
|
+
@connection = self.class.new_client(@connection_options)
|
520
|
+
configure_connection
|
521
|
+
end
|
490
522
|
end
|
491
523
|
end
|
492
524
|
end
|