activerecord-sqlserver-adapter 6.0.0.rc1 → 6.1.0.0.rc1
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/.gitignore +1 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +18 -23
- data/Gemfile +11 -5
- data/Guardfile +9 -8
- data/README.md +32 -3
- data/RUNNING_UNIT_TESTS.md +1 -1
- data/Rakefile +12 -16
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +4 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +3 -13
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +8 -5
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +0 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +58 -43
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +7 -12
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -3
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +15 -15
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +22 -3
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -10
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +131 -105
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +6 -8
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +25 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +1 -5
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +6 -10
- data/lib/active_record/connection_adapters/sqlserver/type.rb +36 -35
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +0 -1
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +6 -9
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +1 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +1 -2
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +1 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +1 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +8 -11
- data/lib/active_record/connection_adapters/sqlserver/version.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +170 -136
- data/lib/active_record/connection_adapters/sqlserver_column.rb +16 -1
- data/lib/active_record/sqlserver_base.rb +9 -15
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +36 -39
- data/lib/activerecord-sqlserver-adapter.rb +1 -1
- data/lib/arel/visitors/sqlserver.rb +126 -50
- data/lib/arel_sqlserver.rb +2 -2
- data/test/cases/adapter_test_sqlserver.rb +203 -190
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/change_column_null_test_sqlserver.rb +12 -12
- data/test/cases/coerced_tests.rb +656 -318
- data/test/cases/column_test_sqlserver.rb +285 -284
- data/test/cases/connection_test_sqlserver.rb +15 -20
- data/test/cases/disconnected_test_sqlserver.rb +39 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +26 -19
- data/test/cases/fetch_test_sqlserver.rb +14 -22
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +12 -18
- data/test/cases/helper_sqlserver.rb +13 -15
- data/test/cases/in_clause_test_sqlserver.rb +36 -9
- data/test/cases/index_test_sqlserver.rb +13 -15
- data/test/cases/json_test_sqlserver.rb +23 -25
- data/test/cases/lateral_test_sqlserver.rb +35 -0
- data/test/cases/migration_test_sqlserver.rb +71 -26
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +57 -53
- data/test/cases/pessimistic_locking_test_sqlserver.rb +25 -33
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +33 -46
- data/test/cases/schema_dumper_test_sqlserver.rb +121 -108
- data/test/cases/schema_test_sqlserver.rb +18 -26
- data/test/cases/scratchpad_test_sqlserver.rb +2 -4
- data/test/cases/showplan_test_sqlserver.rb +24 -33
- data/test/cases/specific_schema_test_sqlserver.rb +66 -65
- data/test/cases/transaction_test_sqlserver.rb +16 -19
- data/test/cases/trigger_test_sqlserver.rb +12 -12
- data/test/cases/utils_test_sqlserver.rb +68 -70
- data/test/cases/uuid_test_sqlserver.rb +11 -13
- data/test/debug.rb +6 -6
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/migrations/create_clients_and_change_column_null.rb +1 -1
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +2 -4
- data/test/models/sqlserver/booking.rb +1 -1
- data/test/models/sqlserver/customers_view.rb +1 -1
- data/test/models/sqlserver/dollar_table_name.rb +1 -1
- data/test/models/sqlserver/edge_schema.rb +1 -3
- data/test/models/sqlserver/fk_has_fk.rb +1 -1
- data/test/models/sqlserver/fk_has_pk.rb +1 -1
- data/test/models/sqlserver/natural_pk_data.rb +2 -2
- data/test/models/sqlserver/natural_pk_int_data.rb +1 -1
- data/test/models/sqlserver/no_pk_data.rb +1 -1
- data/test/models/sqlserver/object_default.rb +1 -1
- data/test/models/sqlserver/quoted_table.rb +2 -2
- data/test/models/sqlserver/quoted_view_1.rb +1 -1
- data/test/models/sqlserver/quoted_view_2.rb +1 -1
- data/test/models/sqlserver/sst_memory.rb +1 -1
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/models/sqlserver/string_default.rb +1 -1
- data/test/models/sqlserver/string_defaults_big_view.rb +1 -1
- data/test/models/sqlserver/string_defaults_view.rb +1 -1
- data/test/models/sqlserver/tinyint_pk.rb +1 -1
- data/test/models/sqlserver/trigger.rb +2 -2
- data/test/models/sqlserver/trigger_history.rb +1 -1
- data/test/models/sqlserver/upper.rb +1 -1
- data/test/models/sqlserver/uppered.rb +1 -1
- data/test/models/sqlserver/uuid.rb +1 -1
- data/test/schema/sqlserver_specific_schema.rb +36 -21
- data/test/support/coerceable_test_sqlserver.rb +1 -4
- data/test/support/connection_reflection.rb +1 -2
- data/test/support/core_ext/query_cache.rb +1 -1
- data/test/support/load_schema_sqlserver.rb +3 -5
- 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/minitest_sqlserver.rb +1 -1
- data/test/support/paths_sqlserver.rb +9 -11
- data/test/support/rake_helpers.rb +12 -10
- data/test/support/sql_counter_sqlserver.rb +14 -16
- data/test/support/test_in_memory_oltp.rb +7 -7
- metadata +31 -11
- data/.travis.yml +0 -23
|
@@ -5,7 +5,6 @@ module ActiveRecord
|
|
|
5
5
|
module SQLServer
|
|
6
6
|
module Type
|
|
7
7
|
class Varchar < Char
|
|
8
|
-
|
|
9
8
|
def initialize(**args)
|
|
10
9
|
super
|
|
11
10
|
@limit = 8000 if @limit.to_i == 0
|
|
@@ -16,12 +15,11 @@ module ActiveRecord
|
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
def sqlserver_type
|
|
19
|
-
|
|
18
|
+
"varchar".yield_self do |type|
|
|
20
19
|
type += "(#{limit})" if limit
|
|
21
20
|
type
|
|
22
21
|
end
|
|
23
22
|
end
|
|
24
|
-
|
|
25
23
|
end
|
|
26
24
|
end
|
|
27
25
|
end
|
|
@@ -5,7 +5,6 @@ module ActiveRecord
|
|
|
5
5
|
module SQLServer
|
|
6
6
|
module Type
|
|
7
7
|
class VarcharMax < Varchar
|
|
8
|
-
|
|
9
8
|
def initialize(**args)
|
|
10
9
|
super
|
|
11
10
|
@limit = 2_147_483_647
|
|
@@ -18,7 +17,6 @@ module ActiveRecord
|
|
|
18
17
|
def sqlserver_type
|
|
19
18
|
"varchar(max)"
|
|
20
19
|
end
|
|
21
|
-
|
|
22
20
|
end
|
|
23
21
|
end
|
|
24
22
|
end
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "strscan"
|
|
4
4
|
|
|
5
5
|
module ActiveRecord
|
|
6
6
|
module ConnectionAdapters
|
|
7
7
|
module SQLServer
|
|
8
8
|
module Utils
|
|
9
|
-
|
|
10
|
-
QUOTED_STRING_PREFIX = 'N'
|
|
9
|
+
QUOTED_STRING_PREFIX = "N"
|
|
11
10
|
|
|
12
11
|
# Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL
|
|
13
12
|
# Inspiried from Rails PostgreSQL::Name adapter object in their own Utils.
|
|
14
13
|
#
|
|
15
14
|
class Name
|
|
16
|
-
|
|
17
15
|
SEPARATOR = "."
|
|
18
16
|
UNQUOTED_SCANNER = /\]?\./
|
|
19
17
|
QUOTED_SCANNER = /\A\[.*?\]\./
|
|
@@ -56,15 +54,15 @@ module ActiveRecord
|
|
|
56
54
|
end
|
|
57
55
|
|
|
58
56
|
def quoted
|
|
59
|
-
parts.map{ |p| quote(p) if p }.join SEPARATOR
|
|
57
|
+
parts.map { |p| quote(p) if p }.join SEPARATOR
|
|
60
58
|
end
|
|
61
59
|
|
|
62
60
|
def quoted_raw
|
|
63
61
|
quote @raw_name
|
|
64
62
|
end
|
|
65
63
|
|
|
66
|
-
def ==(
|
|
67
|
-
|
|
64
|
+
def ==(other)
|
|
65
|
+
other.class == self.class && other.parts == parts
|
|
68
66
|
end
|
|
69
67
|
alias_method :eql?, :==
|
|
70
68
|
|
|
@@ -77,6 +75,7 @@ module ActiveRecord
|
|
|
77
75
|
def parse_raw_name
|
|
78
76
|
@parts = []
|
|
79
77
|
return if raw_name.blank?
|
|
78
|
+
|
|
80
79
|
scanner = StringScanner.new(raw_name)
|
|
81
80
|
matched = scanner.exist?(QUOTED_CHECKER) ? scanner.scan_until(QUOTED_SCANNER) : scanner.scan_until(UNQUOTED_SCANNER)
|
|
82
81
|
while matched
|
|
@@ -93,7 +92,7 @@ module ActiveRecord
|
|
|
93
92
|
@schema = @parts.first
|
|
94
93
|
end
|
|
95
94
|
rest = scanner.rest
|
|
96
|
-
rest = rest.
|
|
95
|
+
rest = rest.start_with?(".") ? rest[1..-1] : rest[0..-1]
|
|
97
96
|
@object = unquote(rest)
|
|
98
97
|
@parts << @object
|
|
99
98
|
end
|
|
@@ -103,7 +102,7 @@ module ActiveRecord
|
|
|
103
102
|
end
|
|
104
103
|
|
|
105
104
|
def unquote(part)
|
|
106
|
-
if part && part.start_with?(
|
|
105
|
+
if part && part.start_with?("[")
|
|
107
106
|
part[1..-2]
|
|
108
107
|
else
|
|
109
108
|
part
|
|
@@ -113,7 +112,6 @@ module ActiveRecord
|
|
|
113
112
|
def parts
|
|
114
113
|
@parts
|
|
115
114
|
end
|
|
116
|
-
|
|
117
115
|
end
|
|
118
116
|
|
|
119
117
|
extend self
|
|
@@ -141,7 +139,6 @@ module ActiveRecord
|
|
|
141
139
|
def extract_identifiers(name)
|
|
142
140
|
SQLServer::Utils::Name.new(name)
|
|
143
141
|
end
|
|
144
|
-
|
|
145
142
|
end
|
|
146
143
|
end
|
|
147
144
|
end
|
|
@@ -1,40 +1,39 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
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
|
|
30
|
-
require
|
|
31
|
-
require
|
|
32
|
-
require
|
|
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"
|
|
33
33
|
|
|
34
34
|
module ActiveRecord
|
|
35
35
|
module ConnectionAdapters
|
|
36
36
|
class SQLServerAdapter < AbstractAdapter
|
|
37
|
-
|
|
38
37
|
include SQLServer::Version,
|
|
39
38
|
SQLServer::Quoting,
|
|
40
39
|
SQLServer::DatabaseStatements,
|
|
@@ -43,7 +42,7 @@ module ActiveRecord
|
|
|
43
42
|
SQLServer::DatabaseLimits,
|
|
44
43
|
SQLServer::DatabaseTasks
|
|
45
44
|
|
|
46
|
-
ADAPTER_NAME =
|
|
45
|
+
ADAPTER_NAME = "SQLServer".freeze
|
|
47
46
|
|
|
48
47
|
# Default precision for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql)
|
|
49
48
|
DEFAULT_TIME_PRECISION = 7
|
|
@@ -56,17 +55,77 @@ module ActiveRecord
|
|
|
56
55
|
cattr_accessor :showplan_option, instance_accessor: false
|
|
57
56
|
cattr_accessor :lowercase_schema_reflection
|
|
58
57
|
|
|
59
|
-
self.cs_equality_operator =
|
|
58
|
+
self.cs_equality_operator = "COLLATE Latin1_General_CS_AS_WS"
|
|
60
59
|
self.use_output_inserted = true
|
|
61
60
|
self.exclude_output_inserted_table_names = Concurrent::Map.new { false }
|
|
62
61
|
|
|
63
|
-
|
|
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)
|
|
64
126
|
super(connection, logger, config)
|
|
65
|
-
# Our Responsibility
|
|
66
127
|
@connection_options = config
|
|
67
|
-
|
|
68
|
-
initialize_dateformatter
|
|
69
|
-
use_database
|
|
128
|
+
configure_connection
|
|
70
129
|
end
|
|
71
130
|
|
|
72
131
|
# === Abstract Adapter ========================================== #
|
|
@@ -153,6 +212,10 @@ module ActiveRecord
|
|
|
153
212
|
true
|
|
154
213
|
end
|
|
155
214
|
|
|
215
|
+
def supports_optimizer_hints?
|
|
216
|
+
true
|
|
217
|
+
end
|
|
218
|
+
|
|
156
219
|
def supports_lazy_transactions?
|
|
157
220
|
true
|
|
158
221
|
end
|
|
@@ -189,7 +252,8 @@ module ActiveRecord
|
|
|
189
252
|
|
|
190
253
|
def active?
|
|
191
254
|
return false unless @connection
|
|
192
|
-
|
|
255
|
+
|
|
256
|
+
raw_connection_do "SELECT 1"
|
|
193
257
|
true
|
|
194
258
|
rescue *connection_errors
|
|
195
259
|
false
|
|
@@ -219,7 +283,15 @@ module ActiveRecord
|
|
|
219
283
|
|
|
220
284
|
def reset!
|
|
221
285
|
reset_transaction
|
|
222
|
-
do_execute
|
|
286
|
+
do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"
|
|
287
|
+
end
|
|
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
|
|
223
295
|
end
|
|
224
296
|
|
|
225
297
|
# === Abstract Adapter (Misc Support) =========================== #
|
|
@@ -254,6 +326,7 @@ module ActiveRecord
|
|
|
254
326
|
|
|
255
327
|
def database_prefix_remote_server?
|
|
256
328
|
return false if database_prefix.blank?
|
|
329
|
+
|
|
257
330
|
name = SQLServer::Utils.extract_identifiers(database_prefix)
|
|
258
331
|
name.fully_qualified? && name.object.blank?
|
|
259
332
|
end
|
|
@@ -294,31 +367,38 @@ module ActiveRecord
|
|
|
294
367
|
# === Abstract Adapter (Misc Support) =========================== #
|
|
295
368
|
|
|
296
369
|
def initialize_type_map(m = type_map)
|
|
297
|
-
m.register_type %r{.*},
|
|
370
|
+
m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
|
|
371
|
+
|
|
298
372
|
# Exact Numerics
|
|
299
|
-
register_class_with_limit m,
|
|
300
|
-
m.alias_type
|
|
301
|
-
register_class_with_limit m,
|
|
302
|
-
m.alias_type
|
|
303
|
-
m.alias_type
|
|
304
|
-
register_class_with_limit m,
|
|
305
|
-
m.alias_type
|
|
306
|
-
register_class_with_limit m,
|
|
307
|
-
m.alias_type
|
|
308
|
-
m.register_type
|
|
373
|
+
register_class_with_limit m, "bigint(8)", SQLServer::Type::BigInteger
|
|
374
|
+
m.alias_type "bigint", "bigint(8)"
|
|
375
|
+
register_class_with_limit m, "int(4)", SQLServer::Type::Integer
|
|
376
|
+
m.alias_type "integer", "int(4)"
|
|
377
|
+
m.alias_type "int", "int(4)"
|
|
378
|
+
register_class_with_limit m, "smallint(2)", SQLServer::Type::SmallInteger
|
|
379
|
+
m.alias_type "smallint", "smallint(2)"
|
|
380
|
+
register_class_with_limit m, "tinyint(1)", SQLServer::Type::TinyInteger
|
|
381
|
+
m.alias_type "tinyint", "tinyint(1)"
|
|
382
|
+
m.register_type "bit", SQLServer::Type::Boolean.new
|
|
309
383
|
m.register_type %r{\Adecimal}i do |sql_type|
|
|
310
|
-
scale
|
|
384
|
+
scale = extract_scale(sql_type)
|
|
311
385
|
precision = extract_precision(sql_type)
|
|
312
|
-
|
|
386
|
+
if scale == 0
|
|
387
|
+
SQLServer::Type::DecimalWithoutScale.new(precision: precision)
|
|
388
|
+
else
|
|
389
|
+
SQLServer::Type::Decimal.new(precision: precision, scale: scale)
|
|
390
|
+
end
|
|
313
391
|
end
|
|
314
|
-
m.alias_type %r{\Anumeric}i,
|
|
315
|
-
m.register_type
|
|
316
|
-
m.register_type
|
|
392
|
+
m.alias_type %r{\Anumeric}i, "decimal"
|
|
393
|
+
m.register_type "money", SQLServer::Type::Money.new
|
|
394
|
+
m.register_type "smallmoney", SQLServer::Type::SmallMoney.new
|
|
395
|
+
|
|
317
396
|
# Approximate Numerics
|
|
318
|
-
m.register_type
|
|
319
|
-
m.register_type
|
|
397
|
+
m.register_type "float", SQLServer::Type::Float.new
|
|
398
|
+
m.register_type "real", SQLServer::Type::Real.new
|
|
399
|
+
|
|
320
400
|
# Date and Time
|
|
321
|
-
m.register_type
|
|
401
|
+
m.register_type "date", SQLServer::Type::Date.new
|
|
322
402
|
m.register_type %r{\Adatetime} do |sql_type|
|
|
323
403
|
precision = extract_precision(sql_type)
|
|
324
404
|
if precision
|
|
@@ -327,38 +407,44 @@ module ActiveRecord
|
|
|
327
407
|
SQLServer::Type::DateTime.new
|
|
328
408
|
end
|
|
329
409
|
end
|
|
330
|
-
m.register_type
|
|
410
|
+
m.register_type %r{\Adatetimeoffset}i do |sql_type|
|
|
331
411
|
precision = extract_precision(sql_type)
|
|
332
412
|
SQLServer::Type::DateTimeOffset.new precision: precision
|
|
333
413
|
end
|
|
334
|
-
m.register_type
|
|
414
|
+
m.register_type "smalldatetime", SQLServer::Type::SmallDateTime.new
|
|
335
415
|
m.register_type %r{\Atime}i do |sql_type|
|
|
336
416
|
precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION
|
|
337
417
|
SQLServer::Type::Time.new precision: precision
|
|
338
418
|
end
|
|
419
|
+
|
|
339
420
|
# Character Strings
|
|
340
421
|
register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char
|
|
341
422
|
register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar
|
|
342
|
-
m.register_type
|
|
343
|
-
m.register_type
|
|
423
|
+
m.register_type "varchar(max)", SQLServer::Type::VarcharMax.new
|
|
424
|
+
m.register_type "text", SQLServer::Type::Text.new
|
|
425
|
+
|
|
344
426
|
# Unicode Character Strings
|
|
345
427
|
register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar
|
|
346
428
|
register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
|
|
347
|
-
m.alias_type
|
|
348
|
-
m.register_type
|
|
349
|
-
m.register_type
|
|
350
|
-
m.register_type
|
|
429
|
+
m.alias_type "string", "nvarchar(4000)"
|
|
430
|
+
m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
|
|
431
|
+
m.register_type "nvarchar(max)", SQLServer::Type::UnicodeVarcharMax.new
|
|
432
|
+
m.register_type "ntext", SQLServer::Type::UnicodeText.new
|
|
433
|
+
|
|
351
434
|
# Binary Strings
|
|
352
435
|
register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
|
|
353
436
|
register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary
|
|
354
|
-
m.register_type
|
|
437
|
+
m.register_type "varbinary(max)", SQLServer::Type::VarbinaryMax.new
|
|
438
|
+
|
|
355
439
|
# Other Data Types
|
|
356
|
-
m.register_type
|
|
357
|
-
m.register_type
|
|
440
|
+
m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new
|
|
441
|
+
m.register_type "timestamp", SQLServer::Type::Timestamp.new
|
|
358
442
|
end
|
|
359
443
|
|
|
360
444
|
def translate_exception(e, message:, sql:, binds:)
|
|
361
445
|
case message
|
|
446
|
+
when /(SQL Server client is not connected)|(failed to execute statement)/i
|
|
447
|
+
ConnectionNotEstablished.new(message)
|
|
362
448
|
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
|
|
363
449
|
RecordNotUnique.new(message, sql: sql, binds: binds)
|
|
364
450
|
when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i
|
|
@@ -392,83 +478,23 @@ module ActiveRecord
|
|
|
392
478
|
|
|
393
479
|
# === SQLServer Specific (Connection Management) ================ #
|
|
394
480
|
|
|
395
|
-
def connect
|
|
396
|
-
config = @connection_options
|
|
397
|
-
@connection = case config[:mode]
|
|
398
|
-
when :dblib
|
|
399
|
-
dblib_connect(config)
|
|
400
|
-
end
|
|
401
|
-
@spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first
|
|
402
|
-
@version_year = version_year
|
|
403
|
-
configure_connection
|
|
404
|
-
end
|
|
405
|
-
|
|
406
481
|
def connection_errors
|
|
407
482
|
@connection_errors ||= [].tap do |errors|
|
|
408
483
|
errors << TinyTds::Error if defined?(TinyTds::Error)
|
|
409
484
|
end
|
|
410
485
|
end
|
|
411
486
|
|
|
412
|
-
def
|
|
413
|
-
TinyTds::Client.new(
|
|
414
|
-
dataserver: config[:dataserver],
|
|
415
|
-
host: config[:host],
|
|
416
|
-
port: config[:port],
|
|
417
|
-
username: config[:username],
|
|
418
|
-
password: config[:password],
|
|
419
|
-
database: config[:database],
|
|
420
|
-
tds_version: config[:tds_version] || '7.3',
|
|
421
|
-
appname: config_appname(config),
|
|
422
|
-
login_timeout: config_login_timeout(config),
|
|
423
|
-
timeout: config_timeout(config),
|
|
424
|
-
encoding: config_encoding(config),
|
|
425
|
-
azure: config[:azure],
|
|
426
|
-
contained: config[:contained]
|
|
427
|
-
).tap do |client|
|
|
428
|
-
if config[:azure]
|
|
429
|
-
client.execute('SET ANSI_NULLS ON').do
|
|
430
|
-
client.execute('SET ANSI_NULL_DFLT_ON ON').do
|
|
431
|
-
client.execute('SET ANSI_PADDING ON').do
|
|
432
|
-
client.execute('SET ANSI_WARNINGS ON').do
|
|
433
|
-
else
|
|
434
|
-
client.execute('SET ANSI_DEFAULTS ON').do
|
|
435
|
-
end
|
|
436
|
-
client.execute('SET QUOTED_IDENTIFIER ON').do
|
|
437
|
-
client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
|
|
438
|
-
client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
|
|
439
|
-
client.execute('SET TEXTSIZE 2147483647').do
|
|
440
|
-
client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do
|
|
441
|
-
end
|
|
442
|
-
end
|
|
443
|
-
|
|
444
|
-
def config_appname(config)
|
|
445
|
-
config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil
|
|
446
|
-
end
|
|
447
|
-
|
|
448
|
-
def config_login_timeout(config)
|
|
449
|
-
config[:login_timeout].present? ? config[:login_timeout].to_i : nil
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
def config_timeout(config)
|
|
453
|
-
config[:timeout].present? ? config[:timeout].to_i / 1000 : nil
|
|
454
|
-
end
|
|
455
|
-
|
|
456
|
-
def config_encoding(config)
|
|
457
|
-
config[:encoding].present? ? config[:encoding] : nil
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
def configure_connection ; end
|
|
461
|
-
|
|
462
|
-
def configure_application_name ; end
|
|
487
|
+
def configure_application_name; end
|
|
463
488
|
|
|
464
489
|
def initialize_dateformatter
|
|
465
490
|
@database_dateformat = user_options_dateformat
|
|
466
491
|
a, b, c = @database_dateformat.each_char.to_a
|
|
467
|
-
|
|
492
|
+
|
|
493
|
+
[a, b, c].each { |f| f.upcase! if f == "y" }
|
|
468
494
|
dateformat = "%#{a}-%#{b}-%#{c}"
|
|
469
495
|
::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
470
496
|
::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
471
|
-
::Time::DATE_FORMATS[:_sqlserver_time] =
|
|
497
|
+
::Time::DATE_FORMATS[:_sqlserver_time] = "%H:%M:%S"
|
|
472
498
|
::Time::DATE_FORMATS[:_sqlserver_datetime] = "#{dateformat} %H:%M:%S"
|
|
473
499
|
::Time::DATE_FORMATS[:_sqlserver_datetimeoffset] = lambda { |time|
|
|
474
500
|
time.strftime "#{dateformat} %H:%M:%S.%9N #{time.formatted_offset}"
|
|
@@ -477,13 +503,21 @@ module ActiveRecord
|
|
|
477
503
|
|
|
478
504
|
def version_year
|
|
479
505
|
return 2016 if sqlserver_version =~ /vNext/
|
|
506
|
+
|
|
480
507
|
/SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i
|
|
481
|
-
rescue StandardError
|
|
508
|
+
rescue StandardError
|
|
482
509
|
2016
|
|
483
510
|
end
|
|
484
511
|
|
|
485
512
|
def sqlserver_version
|
|
486
|
-
@sqlserver_version ||= _raw_select(
|
|
513
|
+
@sqlserver_version ||= _raw_select("SELECT @@version", fetch: :rows).first.first.to_s
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
private
|
|
517
|
+
|
|
518
|
+
def connect
|
|
519
|
+
@connection = self.class.new_client(@connection_options)
|
|
520
|
+
configure_connection
|
|
487
521
|
end
|
|
488
522
|
end
|
|
489
523
|
end
|