activerecord-jdbcsqlserver-adapter 50.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 +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +27 -0
- data/CHANGELOG.md +124 -0
- data/CODE_OF_CONDUCT.md +31 -0
- data/Dockerfile +20 -0
- data/Gemfile +77 -0
- data/Guardfile +29 -0
- data/MIT-LICENSE +20 -0
- data/RAILS5-TODO.md +5 -0
- data/README.md +93 -0
- data/RUNNING_UNIT_TESTS.md +96 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/activerecord-jdbcsqlserver-adapter.gemspec +21 -0
- data/appveyor.yml +39 -0
- data/docker-compose.ci.yml +11 -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/date_time.rb +58 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +47 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +362 -0
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +67 -0
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +7 -0
- data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +192 -0
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +99 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +34 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +517 -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/sql_type_metadata.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +112 -0
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +64 -0
- data/lib/active_record/connection_adapters/sqlserver/type.rb +49 -0
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +32 -0
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +30 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +61 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +71 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +23 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +29 -0
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +68 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +93 -0
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +21 -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 +19 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +36 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +146 -0
- data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +445 -0
- data/lib/active_record/connection_adapters/sqlserver_column.rb +28 -0
- data/lib/active_record/jdbc_sqlserver_connection_methods.rb +31 -0
- data/lib/active_record/sqlserver_base.rb +16 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +131 -0
- data/lib/activerecord-jdbcsqlserver-adapter.rb +24 -0
- data/lib/activerecord-sqlserver-adapter.rb +1 -0
- data/lib/arel/visitors/sqlserver.rb +205 -0
- data/lib/arel_sqlserver.rb +3 -0
- data/test/appveyor/dbsetup.ps1 +27 -0
- data/test/appveyor/dbsetup.sql +11 -0
- data/test/bin/wait-for.sh +79 -0
- data/test/cases/adapter_test_sqlserver.rb +430 -0
- data/test/cases/coerced_tests.rb +845 -0
- data/test/cases/column_test_sqlserver.rb +812 -0
- data/test/cases/connection_test_sqlserver.rb +71 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +45 -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 +44 -0
- data/test/cases/index_test_sqlserver.rb +47 -0
- data/test/cases/json_test_sqlserver.rb +32 -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 +94 -0
- data/test/cases/rake_test_sqlserver.rb +169 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +234 -0
- data/test/cases/schema_test_sqlserver.rb +54 -0
- data/test/cases/scratchpad_test_sqlserver.rb +8 -0
- data/test/cases/showplan_test_sqlserver.rb +65 -0
- data/test/cases/specific_schema_test_sqlserver.rb +180 -0
- data/test/cases/transaction_test_sqlserver.rb +91 -0
- data/test/cases/utils_test_sqlserver.rb +129 -0
- data/test/cases/uuid_test_sqlserver.rb +49 -0
- data/test/config.yml +38 -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 +8 -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/sst_memory.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/enable-in-memory-oltp.sql +81 -0
- data/test/schema/sqlserver_specific_schema.rb +238 -0
- data/test/support/coerceable_test_sqlserver.rb +49 -0
- data/test/support/connection_reflection.rb +34 -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 +28 -0
- data/test/support/test_in_memory_oltp.rb +15 -0
- metadata +310 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module ActiveRecord
|
|
2
|
+
module ConnectionAdapters
|
|
3
|
+
module SQLServer
|
|
4
|
+
module Type
|
|
5
|
+
class VarcharMax < Varchar
|
|
6
|
+
|
|
7
|
+
def initialize(*args)
|
|
8
|
+
super
|
|
9
|
+
@limit = 2_147_483_647
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def type
|
|
13
|
+
:varchar_max
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def sqlserver_type
|
|
17
|
+
'varchar(max)'.freeze
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
require 'strscan'
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
module ConnectionAdapters
|
|
5
|
+
module SQLServer
|
|
6
|
+
module Utils
|
|
7
|
+
|
|
8
|
+
QUOTED_STRING_PREFIX = 'N'
|
|
9
|
+
|
|
10
|
+
# Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL
|
|
11
|
+
# Inspiried from Rails PostgreSQL::Name adapter object in their own Utils.
|
|
12
|
+
#
|
|
13
|
+
class Name
|
|
14
|
+
|
|
15
|
+
SEPARATOR = "."
|
|
16
|
+
UNQUOTED_SCANNER = /\]?\./
|
|
17
|
+
QUOTED_SCANNER = /\A\[.*?\]\./
|
|
18
|
+
QUOTED_CHECKER = /\A\[/
|
|
19
|
+
|
|
20
|
+
attr_reader :server, :database, :schema, :object
|
|
21
|
+
attr_reader :raw_name
|
|
22
|
+
|
|
23
|
+
def initialize(name)
|
|
24
|
+
@raw_name = name.to_s
|
|
25
|
+
parse_raw_name
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def object_quoted
|
|
29
|
+
quote object
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def schema_quoted
|
|
33
|
+
schema ? quote(schema) : schema
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def database_quoted
|
|
37
|
+
database ? quote(database) : database
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def server_quoted
|
|
41
|
+
server ? quote(server) : server
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def fully_qualified_database_quoted
|
|
45
|
+
[server_quoted, database_quoted].compact.join(SEPARATOR)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def fully_qualified?
|
|
49
|
+
parts.compact.size == 4
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_s
|
|
53
|
+
quoted
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def quoted
|
|
57
|
+
parts.map{ |p| quote(p) if p }.join SEPARATOR
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def quoted_raw
|
|
61
|
+
quote @raw_name
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def ==(o)
|
|
65
|
+
o.class == self.class && o.parts == parts
|
|
66
|
+
end
|
|
67
|
+
alias_method :eql?, :==
|
|
68
|
+
|
|
69
|
+
def hash
|
|
70
|
+
parts.hash
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
protected
|
|
74
|
+
|
|
75
|
+
def parse_raw_name
|
|
76
|
+
@parts = []
|
|
77
|
+
return if raw_name.blank?
|
|
78
|
+
scanner = StringScanner.new(raw_name)
|
|
79
|
+
matched = scanner.exist?(QUOTED_CHECKER) ? scanner.scan_until(QUOTED_SCANNER) : scanner.scan_until(UNQUOTED_SCANNER)
|
|
80
|
+
while matched
|
|
81
|
+
part = matched[0..-2]
|
|
82
|
+
@parts << (part.blank? ? nil : unquote(part))
|
|
83
|
+
matched = scanner.exist?(QUOTED_CHECKER) ? scanner.scan_until(QUOTED_SCANNER) : scanner.scan_until(UNQUOTED_SCANNER)
|
|
84
|
+
end
|
|
85
|
+
case @parts.length
|
|
86
|
+
when 3
|
|
87
|
+
@server, @database, @schema = @parts
|
|
88
|
+
when 2
|
|
89
|
+
@database, @schema = @parts
|
|
90
|
+
when 1
|
|
91
|
+
@schema = @parts.first
|
|
92
|
+
end
|
|
93
|
+
rest = scanner.rest
|
|
94
|
+
rest = rest.starts_with?('.') ? rest[1..-1] : rest[0..-1]
|
|
95
|
+
@object = unquote(rest)
|
|
96
|
+
@parts << @object
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def quote(part)
|
|
100
|
+
part =~ /\A\[.*\]\z/ ? part : "[#{part.to_s.gsub(']', ']]')}]"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def unquote(part)
|
|
104
|
+
if part && part.start_with?('[')
|
|
105
|
+
part[1..-2]
|
|
106
|
+
else
|
|
107
|
+
part
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def parts
|
|
112
|
+
@parts
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
extend self
|
|
118
|
+
|
|
119
|
+
def quote_string(s)
|
|
120
|
+
s.to_s.gsub /\'/, "''"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def quote_string_single(s)
|
|
124
|
+
"'#{quote_string(s)}'"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def quote_string_single_national(s)
|
|
128
|
+
"#{QUOTED_STRING_PREFIX}'#{quote_string(s)}'"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def quoted_raw(name)
|
|
132
|
+
SQLServer::Utils::Name.new(name).quoted_raw
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def unquote_string(s)
|
|
136
|
+
s.to_s.gsub(/\'\'/, "'")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def extract_identifiers(name)
|
|
140
|
+
SQLServer::Utils::Name.new(name)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,445 @@
|
|
|
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' unless defined? JRUBY_VERSION
|
|
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_creation'
|
|
17
|
+
require 'active_record/connection_adapters/sqlserver/schema_dumper'
|
|
18
|
+
require 'active_record/connection_adapters/sqlserver/schema_statements'
|
|
19
|
+
require 'active_record/connection_adapters/sqlserver/sql_type_metadata'
|
|
20
|
+
require 'active_record/connection_adapters/sqlserver/showplan'
|
|
21
|
+
require 'active_record/connection_adapters/sqlserver/table_definition'
|
|
22
|
+
require 'active_record/connection_adapters/sqlserver/quoting'
|
|
23
|
+
require 'active_record/connection_adapters/sqlserver/utils'
|
|
24
|
+
require 'active_record/sqlserver_base'
|
|
25
|
+
require 'active_record/connection_adapters/sqlserver_column'
|
|
26
|
+
require 'active_record/tasks/sqlserver_database_tasks'
|
|
27
|
+
|
|
28
|
+
module ActiveRecord
|
|
29
|
+
module ConnectionAdapters
|
|
30
|
+
class SQLServerAdapter < AbstractAdapter
|
|
31
|
+
|
|
32
|
+
include SQLServer::Version,
|
|
33
|
+
SQLServer::Quoting,
|
|
34
|
+
SQLServer::DatabaseStatements,
|
|
35
|
+
SQLServer::Showplan,
|
|
36
|
+
SQLServer::SchemaDumper,
|
|
37
|
+
SQLServer::SchemaStatements,
|
|
38
|
+
SQLServer::DatabaseLimits,
|
|
39
|
+
SQLServer::DatabaseTasks
|
|
40
|
+
|
|
41
|
+
USING_JDBC_DRIVER = defined? JRUBY_VERSION
|
|
42
|
+
|
|
43
|
+
if USING_JDBC_DRIVER
|
|
44
|
+
include ArJdbc::Abstract::ConnectionManagement
|
|
45
|
+
include ArJdbc::Util::QuotedCache
|
|
46
|
+
prepend ArJdbc::Abstract::Core
|
|
47
|
+
prepend ArJdbc::Abstract::DatabaseStatements
|
|
48
|
+
prepend ArJdbc::Abstract::StatementCache
|
|
49
|
+
prepend ArJdbc::Abstract::TransactionSupport
|
|
50
|
+
prepend SQLServer::JDBCOverrides
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
ADAPTER_NAME = 'SQLServer'.freeze
|
|
54
|
+
|
|
55
|
+
attr_reader :spid
|
|
56
|
+
|
|
57
|
+
cattr_accessor :cs_equality_operator, instance_accessor: false
|
|
58
|
+
cattr_accessor :use_output_inserted, instance_accessor: false
|
|
59
|
+
cattr_accessor :showplan_option, instance_accessor: false
|
|
60
|
+
cattr_accessor :lowercase_schema_reflection
|
|
61
|
+
|
|
62
|
+
self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS'
|
|
63
|
+
self.use_output_inserted = true
|
|
64
|
+
|
|
65
|
+
def initialize(connection, logger = nil, config = {})
|
|
66
|
+
super(connection, logger, config)
|
|
67
|
+
# Our Responsibility
|
|
68
|
+
@connection_options = config
|
|
69
|
+
connect
|
|
70
|
+
initialize_dateformatter
|
|
71
|
+
use_database
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# === Abstract Adapter ========================================== #
|
|
75
|
+
|
|
76
|
+
def arel_visitor
|
|
77
|
+
Arel::Visitors::SQLServer.new self
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def valid_type?(type)
|
|
81
|
+
!native_database_types[type].nil?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def schema_creation
|
|
85
|
+
SQLServer::SchemaCreation.new self
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def supports_migrations?
|
|
89
|
+
true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def supports_primary_key?
|
|
93
|
+
true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def supports_ddl_transactions?
|
|
97
|
+
true
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def supports_bulk_alter?
|
|
101
|
+
false
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def supports_advisory_locks?
|
|
105
|
+
false
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def supports_index_sort_order?
|
|
109
|
+
true
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def supports_partial_index?
|
|
113
|
+
true
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def supports_expression_index?
|
|
117
|
+
false
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def supports_explain?
|
|
121
|
+
true
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def supports_transaction_isolation?
|
|
125
|
+
true
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def supports_indexes_in_create?
|
|
129
|
+
false
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def supports_foreign_keys?
|
|
133
|
+
true
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def supports_views?
|
|
137
|
+
true
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def supports_datetime_with_precision?
|
|
141
|
+
true
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def supports_json?
|
|
145
|
+
@version_year >= 2016
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def supports_comments?
|
|
149
|
+
false
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def supports_comments_in_create?
|
|
153
|
+
false
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def supports_in_memory_oltp?
|
|
157
|
+
@version_year >= 2014
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def disable_referential_integrity
|
|
161
|
+
tables = tables_with_referential_integrity
|
|
162
|
+
tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" }
|
|
163
|
+
yield
|
|
164
|
+
ensure
|
|
165
|
+
tables.each { |t| do_execute "ALTER TABLE #{t} CHECK CONSTRAINT ALL" }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# === Abstract Adapter (Connection Management) ================== #
|
|
169
|
+
|
|
170
|
+
def active?
|
|
171
|
+
return false unless @connection
|
|
172
|
+
raw_connection_do 'SELECT 1'
|
|
173
|
+
true
|
|
174
|
+
rescue *connection_errors
|
|
175
|
+
false
|
|
176
|
+
end unless USING_JDBC_DRIVER # Core takes care of this
|
|
177
|
+
|
|
178
|
+
def reconnect!
|
|
179
|
+
super
|
|
180
|
+
disconnect! unless USING_JDBC_DRIVER # Don't need to disconnect in JDBC
|
|
181
|
+
connect
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def disconnect!
|
|
185
|
+
super
|
|
186
|
+
case @connection_options[:mode]
|
|
187
|
+
when :dblib
|
|
188
|
+
@connection.close rescue nil
|
|
189
|
+
end
|
|
190
|
+
@connection = nil unless USING_JDBC_DRIVER
|
|
191
|
+
@spid = nil
|
|
192
|
+
@collation = nil
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def clear_cache!
|
|
196
|
+
@view_information = nil
|
|
197
|
+
super
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def reset!
|
|
201
|
+
reset_transaction
|
|
202
|
+
do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# === Abstract Adapter (Misc Support) =========================== #
|
|
206
|
+
|
|
207
|
+
def tables_with_referential_integrity
|
|
208
|
+
schemas_and_tables = select_rows <<-SQL.strip_heredoc
|
|
209
|
+
SELECT s.name AS schema_name, o.name AS table_name
|
|
210
|
+
FROM sys.foreign_keys i
|
|
211
|
+
INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID
|
|
212
|
+
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
|
|
213
|
+
SQL
|
|
214
|
+
schemas_and_tables.map do |schema_table|
|
|
215
|
+
schema, table = schema_table
|
|
216
|
+
"#{SQLServer::Utils.quoted_raw(schema)}.#{SQLServer::Utils.quoted_raw(table)}"
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def pk_and_sequence_for(table_name)
|
|
221
|
+
pk = primary_key(table_name)
|
|
222
|
+
pk ? [pk, nil] : nil
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# === SQLServer Specific (DB Reflection) ======================== #
|
|
226
|
+
|
|
227
|
+
def sqlserver?
|
|
228
|
+
true
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def sqlserver_azure?
|
|
232
|
+
@sqlserver_azure ||= !!(select_value('SELECT @@version', 'SCHEMA') =~ /Azure/i)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def database_prefix_remote_server?
|
|
236
|
+
return false if database_prefix.blank?
|
|
237
|
+
name = SQLServer::Utils.extract_identifiers(database_prefix)
|
|
238
|
+
name.fully_qualified? && name.object.blank?
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def database_prefix
|
|
242
|
+
@connection_options[:database_prefix]
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def version
|
|
246
|
+
self.class::VERSION
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def inspect
|
|
250
|
+
"#<#{self.class} version: #{version}, mode: #{@connection_options[:mode]}, azure: #{sqlserver_azure?.inspect}>"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], having_clause: [], limit: nil, offset: nil)
|
|
254
|
+
result = from_clause + join_clause + where_clause + having_clause
|
|
255
|
+
result << offset if offset
|
|
256
|
+
result << limit if limit
|
|
257
|
+
result
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
protected
|
|
262
|
+
|
|
263
|
+
# === Abstract Adapter (Misc Support) =========================== #
|
|
264
|
+
|
|
265
|
+
def initialize_type_map(m)
|
|
266
|
+
m.register_type %r{.*}, SQLServer::Type::UnicodeString.new
|
|
267
|
+
# Exact Numerics
|
|
268
|
+
register_class_with_limit m, 'bigint(8)', SQLServer::Type::BigInteger
|
|
269
|
+
m.alias_type 'bigint', 'bigint(8)'
|
|
270
|
+
register_class_with_limit m, 'int(4)', SQLServer::Type::Integer
|
|
271
|
+
m.alias_type 'integer', 'int(4)'
|
|
272
|
+
m.alias_type 'int', 'int(4)'
|
|
273
|
+
register_class_with_limit m, 'smallint(2)', SQLServer::Type::SmallInteger
|
|
274
|
+
m.alias_type 'smallint', 'smallint(2)'
|
|
275
|
+
register_class_with_limit m, 'tinyint(1)', SQLServer::Type::TinyInteger
|
|
276
|
+
m.alias_type 'tinyint', 'tinyint(1)'
|
|
277
|
+
m.register_type 'bit', SQLServer::Type::Boolean.new
|
|
278
|
+
m.register_type %r{\Adecimal}i do |sql_type|
|
|
279
|
+
scale = extract_scale(sql_type)
|
|
280
|
+
precision = extract_precision(sql_type)
|
|
281
|
+
SQLServer::Type::Decimal.new precision: precision, scale: scale
|
|
282
|
+
end
|
|
283
|
+
m.alias_type %r{\Anumeric}i, 'decimal'
|
|
284
|
+
m.register_type 'money', SQLServer::Type::Money.new
|
|
285
|
+
m.register_type 'smallmoney', SQLServer::Type::SmallMoney.new
|
|
286
|
+
# Approximate Numerics
|
|
287
|
+
m.register_type 'float', SQLServer::Type::Float.new
|
|
288
|
+
m.register_type 'real', SQLServer::Type::Real.new
|
|
289
|
+
# Date and Time
|
|
290
|
+
m.register_type 'date', SQLServer::Type::Date.new
|
|
291
|
+
m.register_type %r{\Adatetime} do |sql_type|
|
|
292
|
+
precision = extract_precision(sql_type)
|
|
293
|
+
if precision
|
|
294
|
+
SQLServer::Type::DateTime2.new precision: precision
|
|
295
|
+
else
|
|
296
|
+
SQLServer::Type::DateTime.new
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
m.register_type %r{\Adatetimeoffset}i do |sql_type|
|
|
300
|
+
precision = extract_precision(sql_type)
|
|
301
|
+
SQLServer::Type::DateTimeOffset.new precision: precision
|
|
302
|
+
end
|
|
303
|
+
m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new
|
|
304
|
+
m.register_type %r{\Atime}i do |sql_type|
|
|
305
|
+
scale = extract_scale(sql_type)
|
|
306
|
+
precision = extract_precision(sql_type)
|
|
307
|
+
SQLServer::Type::Time.new precision: precision
|
|
308
|
+
end
|
|
309
|
+
# Character Strings
|
|
310
|
+
register_class_with_limit m, %r{\Achar}i, SQLServer::Type::Char
|
|
311
|
+
register_class_with_limit m, %r{\Avarchar}i, SQLServer::Type::Varchar
|
|
312
|
+
m.register_type 'varchar(max)', SQLServer::Type::VarcharMax.new
|
|
313
|
+
m.register_type 'text', SQLServer::Type::Text.new
|
|
314
|
+
# Unicode Character Strings
|
|
315
|
+
register_class_with_limit m, %r{\Anchar}i, SQLServer::Type::UnicodeChar
|
|
316
|
+
register_class_with_limit m, %r{\Anvarchar}i, SQLServer::Type::UnicodeVarchar
|
|
317
|
+
m.alias_type 'string', 'nvarchar(4000)'
|
|
318
|
+
m.register_type 'nvarchar(max)', SQLServer::Type::UnicodeVarcharMax.new
|
|
319
|
+
m.register_type 'nvarchar(max)', SQLServer::Type::UnicodeVarcharMax.new
|
|
320
|
+
m.register_type 'ntext', SQLServer::Type::UnicodeText.new
|
|
321
|
+
# Binary Strings
|
|
322
|
+
register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary
|
|
323
|
+
register_class_with_limit m, %r{\Avarbinary}i, SQLServer::Type::Varbinary
|
|
324
|
+
m.register_type 'varbinary(max)', SQLServer::Type::VarbinaryMax.new
|
|
325
|
+
# Other Data Types
|
|
326
|
+
m.register_type 'uniqueidentifier', SQLServer::Type::Uuid.new
|
|
327
|
+
m.register_type 'timestamp', SQLServer::Type::Timestamp.new
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def translate_exception(e, message)
|
|
331
|
+
case message
|
|
332
|
+
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
|
|
333
|
+
RecordNotUnique.new(message)
|
|
334
|
+
when /conflicted with the foreign key constraint/i
|
|
335
|
+
InvalidForeignKey.new(message)
|
|
336
|
+
when /has been chosen as the deadlock victim/i
|
|
337
|
+
DeadlockVictim.new(message)
|
|
338
|
+
when /database .* does not exist/i
|
|
339
|
+
NoDatabaseError.new(message)
|
|
340
|
+
when /data would be truncated/
|
|
341
|
+
ValueTooLong.new(message)
|
|
342
|
+
else
|
|
343
|
+
super
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# === SQLServer Specific (Connection Management) ================ #
|
|
348
|
+
|
|
349
|
+
def connect
|
|
350
|
+
config = @connection_options
|
|
351
|
+
@connection = case config[:mode]
|
|
352
|
+
when :dblib
|
|
353
|
+
dblib_connect(config)
|
|
354
|
+
end
|
|
355
|
+
@spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first
|
|
356
|
+
@version_year = version_year
|
|
357
|
+
configure_connection
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def connection_errors
|
|
361
|
+
@connection_errors ||= [].tap do |errors|
|
|
362
|
+
errors << TinyTds::Error if defined?(TinyTds::Error)
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def dblib_connect(config)
|
|
367
|
+
TinyTds::Client.new(
|
|
368
|
+
dataserver: config[:dataserver],
|
|
369
|
+
host: config[:host],
|
|
370
|
+
port: config[:port],
|
|
371
|
+
username: config[:username],
|
|
372
|
+
password: config[:password],
|
|
373
|
+
database: config[:database],
|
|
374
|
+
tds_version: config[:tds_version] || '7.3',
|
|
375
|
+
appname: config_appname(config),
|
|
376
|
+
login_timeout: config_login_timeout(config),
|
|
377
|
+
timeout: config_timeout(config),
|
|
378
|
+
encoding: config_encoding(config),
|
|
379
|
+
azure: config[:azure],
|
|
380
|
+
contained: config[:contained]
|
|
381
|
+
).tap do |client|
|
|
382
|
+
if config[:azure]
|
|
383
|
+
client.execute('SET ANSI_NULLS ON').do
|
|
384
|
+
client.execute('SET ANSI_NULL_DFLT_ON ON').do
|
|
385
|
+
client.execute('SET ANSI_PADDING ON').do
|
|
386
|
+
client.execute('SET ANSI_WARNINGS ON').do
|
|
387
|
+
else
|
|
388
|
+
client.execute('SET ANSI_DEFAULTS ON').do
|
|
389
|
+
end
|
|
390
|
+
client.execute('SET QUOTED_IDENTIFIER ON').do
|
|
391
|
+
client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do
|
|
392
|
+
client.execute('SET IMPLICIT_TRANSACTIONS OFF').do
|
|
393
|
+
client.execute('SET TEXTSIZE 2147483647').do
|
|
394
|
+
client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def config_appname(config)
|
|
399
|
+
config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def config_login_timeout(config)
|
|
403
|
+
config[:login_timeout].present? ? config[:login_timeout].to_i : nil
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def config_timeout(config)
|
|
407
|
+
config[:timeout].present? ? config[:timeout].to_i / 1000 : nil
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def config_encoding(config)
|
|
411
|
+
config[:encoding].present? ? config[:encoding] : nil
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
def configure_connection ; end
|
|
415
|
+
|
|
416
|
+
def configure_application_name ; end
|
|
417
|
+
|
|
418
|
+
def initialize_dateformatter
|
|
419
|
+
@database_dateformat = user_options_dateformat
|
|
420
|
+
a, b, c = @database_dateformat.each_char.to_a
|
|
421
|
+
[a, b, c].each { |f| f.upcase! if f == 'y' }
|
|
422
|
+
dateformat = "%#{a}-%#{b}-%#{c}"
|
|
423
|
+
::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
424
|
+
::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat
|
|
425
|
+
::Time::DATE_FORMATS[:_sqlserver_time] = '%H:%M:%S'
|
|
426
|
+
::Time::DATE_FORMATS[:_sqlserver_datetime] = "#{dateformat} %H:%M:%S"
|
|
427
|
+
::Time::DATE_FORMATS[:_sqlserver_datetimeoffset] = lambda { |time|
|
|
428
|
+
time.strftime "#{dateformat} %H:%M:%S.%9N #{time.formatted_offset}"
|
|
429
|
+
}
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
def version_year
|
|
433
|
+
return @version_year if defined?(@version_year)
|
|
434
|
+
@version_year = begin
|
|
435
|
+
vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s
|
|
436
|
+
return 2016 if vstring =~ /vNext/
|
|
437
|
+
/SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i
|
|
438
|
+
rescue Exception => e
|
|
439
|
+
2016
|
|
440
|
+
end
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
end
|
|
444
|
+
end
|
|
445
|
+
end
|