activerecord-sqlserver-adapter_new 4.2.15
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 +7 -0
- data/.gitignore +15 -0
- data/CHANGELOG.md +212 -0
- data/CODE_OF_CONDUCT.md +31 -0
- data/Gemfile +61 -0
- data/Guardfile +29 -0
- data/MIT-LICENSE +20 -0
- data/README.md +201 -0
- data/RUNNING_UNIT_TESTS.md +121 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/activerecord-sqlserver-adapter_new.gemspec +20 -0
- data/appveyor.yml +39 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +27 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +40 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +34 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +386 -0
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +68 -0
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +7 -0
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +69 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +114 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +52 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +473 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +66 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +66 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +76 -0
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +57 -0
- data/lib/active_record/connection_adapters/sqlserver/type.rb +46 -0
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +38 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +41 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +31 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +40 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +76 -0
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +136 -0
- data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +405 -0
- data/lib/active_record/connection_adapters/sqlserver_column.rb +53 -0
- data/lib/active_record/sqlserver_base.rb +20 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +131 -0
- data/lib/activerecord-sqlserver-adapter.rb +1 -0
- data/lib/arel/visitors/sqlserver.rb +214 -0
- data/lib/arel_sqlserver.rb +3 -0
- data/test/appveyor/dbsetup.ps1 +27 -0
- data/test/appveyor/dbsetup.sql +11 -0
- data/test/cases/adapter_test_sqlserver.rb +444 -0
- data/test/cases/coerced_tests.rb +713 -0
- data/test/cases/column_test_sqlserver.rb +780 -0
- data/test/cases/connection_test_sqlserver.rb +142 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +44 -0
- data/test/cases/fetch_test_sqlserver.rb +57 -0
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
- data/test/cases/helper_sqlserver.rb +54 -0
- data/test/cases/migration_test_sqlserver.rb +61 -0
- data/test/cases/order_test_sqlserver.rb +147 -0
- data/test/cases/pessimistic_locking_test_sqlserver.rb +90 -0
- data/test/cases/rake_test_sqlserver.rb +163 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +198 -0
- data/test/cases/schema_test_sqlserver.rb +54 -0
- data/test/cases/scratchpad_test_sqlserver.rb +9 -0
- data/test/cases/showplan_test_sqlserver.rb +65 -0
- data/test/cases/specific_schema_test_sqlserver.rb +167 -0
- data/test/cases/transaction_test_sqlserver.rb +66 -0
- data/test/cases/utils_test_sqlserver.rb +129 -0
- data/test/cases/uuid_test_sqlserver.rb +48 -0
- data/test/config.yml +41 -0
- data/test/debug.rb +14 -0
- data/test/fixtures/1px.gif +0 -0
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
- data/test/models/sqlserver/booking.rb +3 -0
- data/test/models/sqlserver/customers_view.rb +3 -0
- data/test/models/sqlserver/datatype.rb +3 -0
- data/test/models/sqlserver/datatype_migration.rb +3 -0
- data/test/models/sqlserver/dollar_table_name.rb +3 -0
- data/test/models/sqlserver/dot_table_name.rb +3 -0
- data/test/models/sqlserver/edge_schema.rb +13 -0
- data/test/models/sqlserver/fk_has_fk.rb +3 -0
- data/test/models/sqlserver/fk_has_pk.rb +3 -0
- data/test/models/sqlserver/natural_pk_data.rb +4 -0
- data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
- data/test/models/sqlserver/no_pk_data.rb +3 -0
- data/test/models/sqlserver/object_default.rb +3 -0
- data/test/models/sqlserver/quoted_table.rb +7 -0
- data/test/models/sqlserver/quoted_view_1.rb +3 -0
- data/test/models/sqlserver/quoted_view_2.rb +3 -0
- data/test/models/sqlserver/string_default.rb +3 -0
- data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
- data/test/models/sqlserver/string_defaults_view.rb +3 -0
- data/test/models/sqlserver/tinyint_pk.rb +3 -0
- data/test/models/sqlserver/upper.rb +3 -0
- data/test/models/sqlserver/uppered.rb +3 -0
- data/test/models/sqlserver/uuid.rb +3 -0
- data/test/schema/datatypes/2012.sql +55 -0
- data/test/schema/sqlserver_specific_schema.rb +207 -0
- data/test/support/coerceable_test_sqlserver.rb +45 -0
- data/test/support/connection_reflection.rb +37 -0
- data/test/support/load_schema_sqlserver.rb +29 -0
- data/test/support/minitest_sqlserver.rb +1 -0
- data/test/support/paths_sqlserver.rb +50 -0
- data/test/support/rake_helpers.rb +41 -0
- data/test/support/sql_counter_sqlserver.rb +32 -0
- metadata +253 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
module ConnectionAdapters
|
|
3
|
+
module SQLServer
|
|
4
|
+
module Type
|
|
5
|
+
class UnicodeVarchar < UnicodeChar
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
super
|
|
9
|
+
@limit = 4000 if @limit.to_i == 0
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def type
|
|
13
|
+
:string
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
module ConnectionAdapters
|
|
3
|
+
module SQLServer
|
|
4
|
+
module Type
|
|
5
|
+
class UnicodeVarcharMax < UnicodeVarchar
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
super
|
|
9
|
+
@limit = 2_147_483_647
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def type
|
|
13
|
+
:text
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
module ConnectionAdapters
|
|
3
|
+
module SQLServer
|
|
4
|
+
module Type
|
|
5
|
+
class Uuid < String
|
|
6
|
+
|
|
7
|
+
ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x
|
|
8
|
+
|
|
9
|
+
alias_method :type_cast_for_database, :type_cast_from_database
|
|
10
|
+
|
|
11
|
+
def type
|
|
12
|
+
:uuid
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def type_cast(value)
|
|
16
|
+
value.to_s[ACCEPTABLE_UUID, 0]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
module ConnectionAdapters
|
|
3
|
+
module SQLServer
|
|
4
|
+
module Type
|
|
5
|
+
class Varbinary < Binary
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
super
|
|
9
|
+
@limit = 8000 if @limit.to_i == 0
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def type
|
|
13
|
+
:varbinary
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
module ConnectionAdapters
|
|
3
|
+
module SQLServer
|
|
4
|
+
module Type
|
|
5
|
+
class VarbinaryMax < Varbinary
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
super
|
|
9
|
+
@limit = 2_147_483_647
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def type
|
|
13
|
+
:binary
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
module ConnectionAdapters
|
|
3
|
+
module SQLServer
|
|
4
|
+
module Type
|
|
5
|
+
class Varchar < Char
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
super
|
|
9
|
+
@limit = 8000 if @limit.to_i == 0
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def type
|
|
13
|
+
:varchar
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
module ConnectionAdapters
|
|
3
|
+
module SQLServer
|
|
4
|
+
module Type
|
|
5
|
+
class VarcharMax < Varchar
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
super
|
|
9
|
+
@limit = 2_147_483_647
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def type
|
|
13
|
+
:varchar_max
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
require 'strscan'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module SQLServer
|
|
6
|
+
module Utils
|
|
7
|
+
|
|
8
|
+
# Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL
|
|
9
|
+
# Inspiried from Rails PostgreSQL::Name adapter object in their own Utils.
|
|
10
|
+
#
|
|
11
|
+
class Name
|
|
12
|
+
|
|
13
|
+
SEPARATOR = "."
|
|
14
|
+
UNQUOTED_SCANNER = /\]?\./
|
|
15
|
+
QUOTED_SCANNER = /\A\[.*?\]\./
|
|
16
|
+
QUOTED_CHECKER = /\A\[/
|
|
17
|
+
|
|
18
|
+
attr_reader :server, :database, :schema, :object
|
|
19
|
+
attr_reader :raw_name
|
|
20
|
+
|
|
21
|
+
def initialize(name)
|
|
22
|
+
@raw_name = name.to_s
|
|
23
|
+
parse_raw_name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def object_quoted
|
|
27
|
+
quote object
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def schema_quoted
|
|
31
|
+
schema ? quote(schema) : schema
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def database_quoted
|
|
35
|
+
database ? quote(database) : database
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def server_quoted
|
|
39
|
+
server ? quote(server) : server
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def fully_qualified_database_quoted
|
|
43
|
+
[server_quoted, database_quoted].compact.join(SEPARATOR)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def fully_qualified?
|
|
47
|
+
parts.compact.size == 4
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_s
|
|
51
|
+
quoted
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def quoted
|
|
55
|
+
parts.map{ |p| quote(p) if p }.join SEPARATOR
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def quoted_raw
|
|
59
|
+
quote @raw_name
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def ==(o)
|
|
63
|
+
o.class == self.class && o.parts == parts
|
|
64
|
+
end
|
|
65
|
+
alias_method :eql?, :==
|
|
66
|
+
|
|
67
|
+
def hash
|
|
68
|
+
parts.hash
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
protected
|
|
72
|
+
|
|
73
|
+
def parse_raw_name
|
|
74
|
+
@parts = []
|
|
75
|
+
return if raw_name.blank?
|
|
76
|
+
scanner = StringScanner.new(raw_name)
|
|
77
|
+
matched = scanner.exist?(QUOTED_CHECKER) ? scanner.scan_until(QUOTED_SCANNER) : scanner.scan_until(UNQUOTED_SCANNER)
|
|
78
|
+
while matched
|
|
79
|
+
part = matched[0..-2]
|
|
80
|
+
@parts << (part.blank? ? nil : unquote(part))
|
|
81
|
+
matched = scanner.exist?(QUOTED_CHECKER) ? scanner.scan_until(QUOTED_SCANNER) : scanner.scan_until(UNQUOTED_SCANNER)
|
|
82
|
+
end
|
|
83
|
+
case @parts.length
|
|
84
|
+
when 3
|
|
85
|
+
@server, @database, @schema = @parts
|
|
86
|
+
when 2
|
|
87
|
+
@database, @schema = @parts
|
|
88
|
+
when 1
|
|
89
|
+
@schema = @parts.first
|
|
90
|
+
end
|
|
91
|
+
rest = scanner.rest
|
|
92
|
+
rest = rest.starts_with?('.') ? rest[1..-1] : rest[0..-1]
|
|
93
|
+
@object = unquote(rest)
|
|
94
|
+
@parts << @object
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def quote(part)
|
|
98
|
+
part =~ /\A\[.*\]\z/ ? part : "[#{part.to_s.gsub(']', ']]')}]"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def unquote(part)
|
|
102
|
+
if part && part.start_with?('[')
|
|
103
|
+
part[1..-2]
|
|
104
|
+
else
|
|
105
|
+
part
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def parts
|
|
110
|
+
@parts
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
extend self
|
|
116
|
+
|
|
117
|
+
def quote_string(s)
|
|
118
|
+
s.to_s.gsub /\'/, "''"
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def quoted_raw(name)
|
|
122
|
+
SQLServer::Utils::Name.new(name).quoted_raw
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def unquote_string(s)
|
|
126
|
+
s.to_s.gsub(/\'\'/, "'")
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def extract_identifiers(name)
|
|
130
|
+
SQLServer::Utils::Name.new(name)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
require 'active_record'
|
|
3
|
+
require 'arel_sqlserver'
|
|
4
|
+
require 'active_record/connection_adapters/abstract_adapter'
|
|
5
|
+
require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
|
|
6
|
+
require 'active_record/connection_adapters/sqlserver/core_ext/explain'
|
|
7
|
+
require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber'
|
|
8
|
+
require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods'
|
|
9
|
+
require 'active_record/connection_adapters/sqlserver/version'
|
|
10
|
+
require 'active_record/connection_adapters/sqlserver/type'
|
|
11
|
+
require 'active_record/connection_adapters/sqlserver/database_limits'
|
|
12
|
+
require 'active_record/connection_adapters/sqlserver/database_statements'
|
|
13
|
+
require 'active_record/connection_adapters/sqlserver/database_tasks'
|
|
14
|
+
require 'active_record/connection_adapters/sqlserver/transaction'
|
|
15
|
+
require 'active_record/connection_adapters/sqlserver/errors'
|
|
16
|
+
require 'active_record/connection_adapters/sqlserver/schema_cache'
|
|
17
|
+
require 'active_record/connection_adapters/sqlserver/schema_creation'
|
|
18
|
+
require 'active_record/connection_adapters/sqlserver/schema_statements'
|
|
19
|
+
require 'active_record/connection_adapters/sqlserver/showplan'
|
|
20
|
+
require 'active_record/connection_adapters/sqlserver/table_definition'
|
|
21
|
+
require 'active_record/connection_adapters/sqlserver/quoting'
|
|
22
|
+
require 'active_record/connection_adapters/sqlserver/utils'
|
|
23
|
+
require 'active_record/sqlserver_base'
|
|
24
|
+
require 'active_record/connection_adapters/sqlserver_column'
|
|
25
|
+
require 'active_record/tasks/sqlserver_database_tasks'
|
|
26
|
+
|
|
27
|
+
module ActiveRecord
|
|
28
|
+
module ConnectionAdapters
|
|
29
|
+
class SQLServerAdapter < AbstractAdapter
|
|
30
|
+
|
|
31
|
+
include SQLServer::Version,
|
|
32
|
+
SQLServer::Quoting,
|
|
33
|
+
SQLServer::DatabaseStatements,
|
|
34
|
+
SQLServer::Showplan,
|
|
35
|
+
SQLServer::SchemaStatements,
|
|
36
|
+
SQLServer::DatabaseLimits,
|
|
37
|
+
SQLServer::DatabaseTasks
|
|
38
|
+
|
|
39
|
+
ADAPTER_NAME = 'SQLServerNew'.freeze
|
|
40
|
+
|
|
41
|
+
attr_reader :spid
|
|
42
|
+
|
|
43
|
+
cattr_accessor :cs_equality_operator, instance_accessor: false
|
|
44
|
+
cattr_accessor :use_output_inserted, instance_accessor: false
|
|
45
|
+
cattr_accessor :lowercase_schema_reflection, :showplan_option
|
|
46
|
+
|
|
47
|
+
self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
|
|
48
|
+
self.use_output_inserted = true
|
|
49
|
+
|
|
50
|
+
def initialize(connection, logger, pool, config)
|
|
51
|
+
super(connection, logger, pool)
|
|
52
|
+
# AbstractAdapter Responsibility
|
|
53
|
+
@schema_cache = SQLServer::SchemaCache.new self
|
|
54
|
+
@visitor = Arel::Visitors::SQLServer.new self
|
|
55
|
+
@prepared_statements = true
|
|
56
|
+
# Our Responsibility
|
|
57
|
+
@connection_options = config
|
|
58
|
+
connect
|
|
59
|
+
@sqlserver_azure = !!(select_value('SELECT @@version', 'SCHEMA') =~ /Azure/i)
|
|
60
|
+
initialize_dateformatter
|
|
61
|
+
use_database
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# === Abstract Adapter ========================================== #
|
|
65
|
+
|
|
66
|
+
def valid_type?(type)
|
|
67
|
+
!native_database_types[type].nil?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def schema_creation
|
|
71
|
+
SQLServer::SchemaCreation.new self
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def adapter_name
|
|
75
|
+
ADAPTER_NAME
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def supports_migrations?
|
|
79
|
+
true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def supports_primary_key?
|
|
83
|
+
true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def supports_count_distinct?
|
|
87
|
+
true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def supports_ddl_transactions?
|
|
91
|
+
true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def supports_bulk_alter?
|
|
95
|
+
false
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def supports_index_sort_order?
|
|
99
|
+
true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def supports_partial_index?
|
|
103
|
+
true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def supports_explain?
|
|
107
|
+
true
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def supports_transaction_isolation?
|
|
111
|
+
true
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def supports_views?
|
|
115
|
+
true
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def supports_foreign_keys?
|
|
119
|
+
true
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def disable_referential_integrity
|
|
123
|
+
tables = tables_with_referential_integrity
|
|
124
|
+
tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" }
|
|
125
|
+
yield
|
|
126
|
+
ensure
|
|
127
|
+
tables.each { |t| do_execute "ALTER TABLE #{t} CHECK CONSTRAINT ALL" }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# === Abstract Adapter (Connection Management) ================== #
|
|
131
|
+
|
|
132
|
+
def active?
|
|
133
|
+
return false unless @connection
|
|
134
|
+
raw_connection_do 'SELECT 1'
|
|
135
|
+
true
|
|
136
|
+
rescue *connection_errors
|
|
137
|
+
false
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def reconnect!
|
|
141
|
+
super
|
|
142
|
+
disconnect!
|
|
143
|
+
connect
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def disconnect!
|
|
147
|
+
super
|
|
148
|
+
@spid = nil
|
|
149
|
+
case @connection_options[:mode]
|
|
150
|
+
when :dblib
|
|
151
|
+
@connection.close rescue nil
|
|
152
|
+
when :odbc
|
|
153
|
+
@connection.disconnect rescue nil
|
|
154
|
+
end
|
|
155
|
+
@connection = nil
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def reset!
|
|
159
|
+
reset_transaction
|
|
160
|
+
do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# === Abstract Adapter (Misc Support) =========================== #
|
|
164
|
+
|
|
165
|
+
def tables_with_referential_integrity
|
|
166
|
+
schemas_and_tables = select_rows <<-SQL.strip_heredoc
|
|
167
|
+
SELECT s.name, o.name
|
|
168
|
+
FROM sys.foreign_keys i
|
|
169
|
+
INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID
|
|
170
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
171
|
+
SQL
|
|
172
|
+
schemas_and_tables.map do |schema_table|
|
|
173
|
+
schema, table = schema_table
|
|
174
|
+
"#{SQLServer::Utils.quoted_raw(schema)}.#{SQLServer::Utils.quoted_raw(table)}"
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def pk_and_sequence_for(table_name)
|
|
179
|
+
pk = primary_key(table_name)
|
|
180
|
+
pk ? [pk, nil] : nil
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def primary_key(table_name)
|
|
184
|
+
schema_cache.columns(table_name).find(&:is_primary?).try(:name) || identity_column(table_name).try(:name)
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# === SQLServer Specific (DB Reflection) ======================== #
|
|
188
|
+
|
|
189
|
+
def sqlserver?
|
|
190
|
+
true
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def sqlserver_azure?
|
|
194
|
+
@sqlserver_azure
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def database_prefix_remote_server?
|
|
198
|
+
return false if database_prefix.blank?
|
|
199
|
+
name = SQLServer::Utils.extract_identifiers(database_prefix)
|
|
200
|
+
name.fully_qualified? && name.object.blank?
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def database_prefix
|
|
204
|
+
@connection_options[:database_prefix]
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def version
|
|
208
|
+
self.class::VERSION
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def inspect
|
|
212
|
+
"#<#{self.class} version: #{version}, mode: #{@connection_options[:mode]}, azure: #{sqlserver_azure?.inspect}>"
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
protected
|
|
217
|
+
|
|
218
|
+
# === Abstract Adapter (Misc Support) =========================== #
|
|
219
|
+
|
|
220
|
+
def initialize_type_map(m)
|
|
221
|
+
m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
|
|
222
|
+
# Exact Numerics
|
|
223
|
+
register_class_with_limit m, 'bigint(8)', SQLServer::Type::BigInteger
|
|
224
|
+
m.alias_type 'bigint', 'bigint(8)'
|
|
225
|
+
register_class_with_limit m, 'int(4)', SQLServer::Type::Integer
|
|
226
|
+
m.alias_type 'integer', 'int(4)'
|
|
227
|
+
m.alias_type 'int', 'int(4)'
|
|
228
|
+
register_class_with_limit m, 'smallint(2)', SQLServer::Type::SmallInteger
|
|
229
|
+
m.alias_type 'smallint', 'smallint(2)'
|
|
230
|
+
register_class_with_limit m, 'tinyint(1)', SQLServer::Type::TinyInteger
|
|
231
|
+
m.alias_type 'tinyint', 'tinyint(1)'
|
|
232
|
+
m.register_type 'bit', SQLServer::Type::Boolean.new
|
|
233
|
+
m.register_type %r{\Adecimal}i do |sql_type|
|
|
234
|
+
scale = extract_scale(sql_type)
|
|
235
|
+
precision = extract_precision(sql_type)
|
|
236
|
+
SQLServer::Type::Decimal.new precision: precision, scale: scale
|
|
237
|
+
end
|
|
238
|
+
m.alias_type %r{\Anumeric}i, 'decimal'
|
|
239
|
+
m.register_type 'money', SQLServer::Type::Money.new
|
|
240
|
+
m.register_type 'smallmoney', SQLServer::Type::SmallMoney.new
|
|
241
|
+
# Approximate Numerics
|
|
242
|
+
m.register_type 'float', SQLServer::Type::Float.new
|
|
243
|
+
m.register_type 'real', SQLServer::Type::Real.new
|
|
244
|
+
# Date and Time
|
|
245
|
+
m.register_type 'date', SQLServer::Type::Date.new
|
|
246
|
+
m.register_type 'datetime', SQLServer::Type::DateTime.new
|
|
247
|
+
m.register_type %r{\Adatetime2}i do |sql_type|
|
|
248
|
+
precision = extract_precision(sql_type)
|
|
249
|
+
SQLServer::Type::DateTime2.new precision: precision
|
|
250
|
+
end
|
|
251
|
+
m.register_type %r{\Adatetimeoffset}i do |sql_type|
|
|
252
|
+
precision = extract_precision(sql_type)
|
|
253
|
+
SQLServer::Type::DateTimeOffset.new precision: precision
|
|
254
|
+
end
|
|
255
|
+
m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new
|
|
256
|
+
m.register_type %r{\Atime}i do |sql_type|
|
|
257
|
+
scale = extract_scale(sql_type)
|
|
258
|
+
precision = extract_precision(sql_type)
|
|
259
|
+
SQLServer::Type::Time.new precision: precision
|
|
260
|
+
end
|
|
261
|
+
# Character Strings
|
|
262
|
+
register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char
|
|
263
|
+
register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar
|
|
264
|
+
m.register_type 'varchar(max)', SQLServer::Type::VarcharMax.new
|
|
265
|
+
m.register_type 'text', SQLServer::Type::Text.new
|
|
266
|
+
# Unicode Character Strings
|
|
267
|
+
register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar
|
|
268
|
+
register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
|
|
269
|
+
m.alias_type 'string', 'nvarchar(4000)'
|
|
270
|
+
m.register_type 'nvarchar(max)', SQLServer::Type::UnicodeVarcharMax.new
|
|
271
|
+
m.register_type 'ntext', SQLServer::Type::UnicodeText.new
|
|
272
|
+
# Binary Strings
|
|
273
|
+
register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
|
|
274
|
+
register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary
|
|
275
|
+
m.register_type 'varbinary(max)', SQLServer::Type::VarbinaryMax.new
|
|
276
|
+
# Other Data Types
|
|
277
|
+
m.register_type 'uniqueidentifier', SQLServer::Type::Uuid.new
|
|
278
|
+
m.register_type 'timestamp', SQLServer::Type::Timestamp.new
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def translate_exception(e, message)
|
|
282
|
+
case message
|
|
283
|
+
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
|
|
284
|
+
RecordNotUnique.new(message, e)
|
|
285
|
+
when /conflicted with the foreign key constraint/i
|
|
286
|
+
InvalidForeignKey.new(message, e)
|
|
287
|
+
when /has been chosen as the deadlock victim/i
|
|
288
|
+
DeadlockVictim.new(message, e)
|
|
289
|
+
when /database .* does not exist/i
|
|
290
|
+
NoDatabaseError.new(message, e)
|
|
291
|
+
else
|
|
292
|
+
super
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# === SQLServer Specific (Connection Management) ================ #
|
|
297
|
+
|
|
298
|
+
def connect
|
|
299
|
+
config = @connection_options
|
|
300
|
+
@connection = case config[:mode]
|
|
301
|
+
when :dblib
|
|
302
|
+
dblib_connect(config)
|
|
303
|
+
when :odbc
|
|
304
|
+
odbc_connect(config)
|
|
305
|
+
end
|
|
306
|
+
@spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first
|
|
307
|
+
configure_connection
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def connection_errors
|
|
311
|
+
@connection_errors ||= [].tap do |errors|
|
|
312
|
+
errors << TinyTds::Error if defined?(TinyTds::Error)
|
|
313
|
+
errors << ODBC::Error if defined?(ODBC::Error)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
def dblib_connect(config)
|
|
318
|
+
TinyTds::Client.new(
|
|
319
|
+
dataserver: config[:dataserver],
|
|
320
|
+
host: config[:host],
|
|
321
|
+
port: config[:port],
|
|
322
|
+
username: config[:username],
|
|
323
|
+
password: config[:password],
|
|
324
|
+
database: config[:database],
|
|
325
|
+
tds_version: config[:tds_version],
|
|
326
|
+
appname: config_appname(config),
|
|
327
|
+
login_timeout: config_login_timeout(config),
|
|
328
|
+
timeout: config_timeout(config),
|
|
329
|
+
encoding: config_encoding(config),
|
|
330
|
+
azure: config[:azure]
|
|
331
|
+
).tap do |client|
|
|
332
|
+
if config[:azure]
|
|
333
|
+
client.execute('SET ANSI_NULLS ON').do
|
|
334
|
+
client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
|
|
335
|
+
client.execute('SET ANSI_NULL_DFLT_ON ON').do
|
|
336
|
+
client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
|
|
337
|
+
client.execute('SET ANSI_PADDING ON').do
|
|
338
|
+
client.execute('SET QUOTED_IDENTIFIER ON').do
|
|
339
|
+
client.execute('SET ANSI_WARNINGS ON').do
|
|
340
|
+
else
|
|
341
|
+
client.execute('SET ANSI_DEFAULTS ON').do
|
|
342
|
+
client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
|
|
343
|
+
client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
|
|
344
|
+
end
|
|
345
|
+
client.execute('SET TEXTSIZE 2147483647').do
|
|
346
|
+
client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def odbc_connect(config)
|
|
351
|
+
if config[:dsn].include?(';')
|
|
352
|
+
driver = ODBC::Driver.new.tap do |d|
|
|
353
|
+
d.name = config[:dsn_name] || 'Driver1'
|
|
354
|
+
d.attrs = config[:dsn].split(';').map { |atr| atr.split('=') }.reject { |kv| kv.size != 2 }.reduce({}) { |a, e| k, v = e ; a[k] = v ; a }
|
|
355
|
+
end
|
|
356
|
+
ODBC::Database.new.drvconnect(driver)
|
|
357
|
+
else
|
|
358
|
+
ODBC.connect config[:dsn], config[:username], config[:password]
|
|
359
|
+
end.tap do |c|
|
|
360
|
+
begin
|
|
361
|
+
c.use_time = true
|
|
362
|
+
c.use_utc = ActiveRecord::Base.default_timezone == :utc
|
|
363
|
+
rescue Exception
|
|
364
|
+
warn 'Ruby ODBC v0.99992 or higher is required.'
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def config_appname(config)
|
|
370
|
+
config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def config_login_timeout(config)
|
|
374
|
+
config[:login_timeout].present? ? config[:login_timeout].to_i : nil
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def config_timeout(config)
|
|
378
|
+
config[:timeout].present? ? config[:timeout].to_i / 1000 : nil
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def config_encoding(config)
|
|
382
|
+
config[:encoding].present? ? config[:encoding] : nil
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def configure_connection ; end
|
|
386
|
+
|
|
387
|
+
def configure_application_name ; end
|
|
388
|
+
|
|
389
|
+
def initialize_dateformatter
|
|
390
|
+
@database_dateformat = user_options_dateformat
|
|
391
|
+
a, b, c = @database_dateformat.each_char.to_a
|
|
392
|
+
[a, b, c].each { |f| f.upcase! if f == 'y' }
|
|
393
|
+
dateformat = "%#{a}-%#{b}-%#{c}"
|
|
394
|
+
::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
395
|
+
::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
396
|
+
::Time::DATE_FORMATS[:_sqlserver_time] = '%H:%M:%S'
|
|
397
|
+
::Time::DATE_FORMATS[:_sqlserver_datetime] = "#{dateformat} %H:%M:%S"
|
|
398
|
+
::Time::DATE_FORMATS[:_sqlserver_datetimeoffset] = lambda { |time|
|
|
399
|
+
time.strftime "#{dateformat} %H:%M:%S.%9N #{time.formatted_offset}"
|
|
400
|
+
}
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|