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.
Files changed (148) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.travis.yml +27 -0
  4. data/CHANGELOG.md +124 -0
  5. data/CODE_OF_CONDUCT.md +31 -0
  6. data/Dockerfile +20 -0
  7. data/Gemfile +77 -0
  8. data/Guardfile +29 -0
  9. data/MIT-LICENSE +20 -0
  10. data/RAILS5-TODO.md +5 -0
  11. data/README.md +93 -0
  12. data/RUNNING_UNIT_TESTS.md +96 -0
  13. data/Rakefile +46 -0
  14. data/VERSION +1 -0
  15. data/activerecord-jdbcsqlserver-adapter.gemspec +21 -0
  16. data/appveyor.yml +39 -0
  17. data/docker-compose.ci.yml +11 -0
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +27 -0
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/date_time.rb +58 -0
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +47 -0
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
  23. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
  24. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +362 -0
  25. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +67 -0
  26. data/lib/active_record/connection_adapters/sqlserver/errors.rb +7 -0
  27. data/lib/active_record/connection_adapters/sqlserver/jdbc_overrides.rb +192 -0
  28. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +99 -0
  29. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +34 -0
  30. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +16 -0
  31. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +517 -0
  32. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +66 -0
  33. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +66 -0
  34. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +22 -0
  35. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +20 -0
  36. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +112 -0
  37. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +64 -0
  38. data/lib/active_record/connection_adapters/sqlserver/type.rb +49 -0
  39. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +19 -0
  40. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +21 -0
  41. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +15 -0
  42. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +32 -0
  43. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +30 -0
  44. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +61 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +71 -0
  46. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
  47. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +23 -0
  48. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +21 -0
  49. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +19 -0
  50. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +15 -0
  51. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +11 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +25 -0
  53. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +19 -0
  54. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +15 -0
  55. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +25 -0
  56. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +29 -0
  57. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
  58. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +19 -0
  59. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +68 -0
  60. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +93 -0
  61. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +19 -0
  62. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +25 -0
  63. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +21 -0
  64. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
  65. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +19 -0
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +26 -0
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +24 -0
  68. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +36 -0
  69. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +26 -0
  70. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +24 -0
  71. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +26 -0
  72. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +24 -0
  73. data/lib/active_record/connection_adapters/sqlserver/utils.rb +146 -0
  74. data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
  75. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +445 -0
  76. data/lib/active_record/connection_adapters/sqlserver_column.rb +28 -0
  77. data/lib/active_record/jdbc_sqlserver_connection_methods.rb +31 -0
  78. data/lib/active_record/sqlserver_base.rb +16 -0
  79. data/lib/active_record/tasks/sqlserver_database_tasks.rb +131 -0
  80. data/lib/activerecord-jdbcsqlserver-adapter.rb +24 -0
  81. data/lib/activerecord-sqlserver-adapter.rb +1 -0
  82. data/lib/arel/visitors/sqlserver.rb +205 -0
  83. data/lib/arel_sqlserver.rb +3 -0
  84. data/test/appveyor/dbsetup.ps1 +27 -0
  85. data/test/appveyor/dbsetup.sql +11 -0
  86. data/test/bin/wait-for.sh +79 -0
  87. data/test/cases/adapter_test_sqlserver.rb +430 -0
  88. data/test/cases/coerced_tests.rb +845 -0
  89. data/test/cases/column_test_sqlserver.rb +812 -0
  90. data/test/cases/connection_test_sqlserver.rb +71 -0
  91. data/test/cases/execute_procedure_test_sqlserver.rb +45 -0
  92. data/test/cases/fetch_test_sqlserver.rb +57 -0
  93. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
  94. data/test/cases/helper_sqlserver.rb +44 -0
  95. data/test/cases/index_test_sqlserver.rb +47 -0
  96. data/test/cases/json_test_sqlserver.rb +32 -0
  97. data/test/cases/migration_test_sqlserver.rb +61 -0
  98. data/test/cases/order_test_sqlserver.rb +147 -0
  99. data/test/cases/pessimistic_locking_test_sqlserver.rb +94 -0
  100. data/test/cases/rake_test_sqlserver.rb +169 -0
  101. data/test/cases/schema_dumper_test_sqlserver.rb +234 -0
  102. data/test/cases/schema_test_sqlserver.rb +54 -0
  103. data/test/cases/scratchpad_test_sqlserver.rb +8 -0
  104. data/test/cases/showplan_test_sqlserver.rb +65 -0
  105. data/test/cases/specific_schema_test_sqlserver.rb +180 -0
  106. data/test/cases/transaction_test_sqlserver.rb +91 -0
  107. data/test/cases/utils_test_sqlserver.rb +129 -0
  108. data/test/cases/uuid_test_sqlserver.rb +49 -0
  109. data/test/config.yml +38 -0
  110. data/test/debug.rb +14 -0
  111. data/test/fixtures/1px.gif +0 -0
  112. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
  113. data/test/models/sqlserver/booking.rb +3 -0
  114. data/test/models/sqlserver/customers_view.rb +3 -0
  115. data/test/models/sqlserver/datatype.rb +3 -0
  116. data/test/models/sqlserver/datatype_migration.rb +8 -0
  117. data/test/models/sqlserver/dollar_table_name.rb +3 -0
  118. data/test/models/sqlserver/dot_table_name.rb +3 -0
  119. data/test/models/sqlserver/edge_schema.rb +13 -0
  120. data/test/models/sqlserver/fk_has_fk.rb +3 -0
  121. data/test/models/sqlserver/fk_has_pk.rb +3 -0
  122. data/test/models/sqlserver/natural_pk_data.rb +4 -0
  123. data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
  124. data/test/models/sqlserver/no_pk_data.rb +3 -0
  125. data/test/models/sqlserver/object_default.rb +3 -0
  126. data/test/models/sqlserver/quoted_table.rb +7 -0
  127. data/test/models/sqlserver/quoted_view_1.rb +3 -0
  128. data/test/models/sqlserver/quoted_view_2.rb +3 -0
  129. data/test/models/sqlserver/sst_memory.rb +3 -0
  130. data/test/models/sqlserver/string_default.rb +3 -0
  131. data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
  132. data/test/models/sqlserver/string_defaults_view.rb +3 -0
  133. data/test/models/sqlserver/tinyint_pk.rb +3 -0
  134. data/test/models/sqlserver/upper.rb +3 -0
  135. data/test/models/sqlserver/uppered.rb +3 -0
  136. data/test/models/sqlserver/uuid.rb +3 -0
  137. data/test/schema/datatypes/2012.sql +55 -0
  138. data/test/schema/enable-in-memory-oltp.sql +81 -0
  139. data/test/schema/sqlserver_specific_schema.rb +238 -0
  140. data/test/support/coerceable_test_sqlserver.rb +49 -0
  141. data/test/support/connection_reflection.rb +34 -0
  142. data/test/support/load_schema_sqlserver.rb +29 -0
  143. data/test/support/minitest_sqlserver.rb +1 -0
  144. data/test/support/paths_sqlserver.rb +50 -0
  145. data/test/support/rake_helpers.rb +41 -0
  146. data/test/support/sql_counter_sqlserver.rb +28 -0
  147. data/test/support/test_in_memory_oltp.rb +15 -0
  148. 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,11 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module SQLServer
4
+ module Version
5
+
6
+ VERSION = File.read(File.expand_path("../../../../../VERSION", __FILE__)).chomp
7
+
8
+ end
9
+ end
10
+ end
11
+ 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