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.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/CHANGELOG.md +212 -0
  4. data/CODE_OF_CONDUCT.md +31 -0
  5. data/Gemfile +61 -0
  6. data/Guardfile +29 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +201 -0
  9. data/RUNNING_UNIT_TESTS.md +121 -0
  10. data/Rakefile +48 -0
  11. data/VERSION +1 -0
  12. data/activerecord-sqlserver-adapter_new.gemspec +20 -0
  13. data/appveyor.yml +39 -0
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +27 -0
  15. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
  16. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +40 -0
  17. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +4 -0
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +34 -0
  19. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
  20. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +386 -0
  21. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +68 -0
  22. data/lib/active_record/connection_adapters/sqlserver/errors.rb +7 -0
  23. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +69 -0
  24. data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +114 -0
  25. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +52 -0
  26. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +473 -0
  27. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +66 -0
  28. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +66 -0
  29. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +22 -0
  30. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +76 -0
  31. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +57 -0
  32. data/lib/active_record/connection_adapters/sqlserver/type.rb +46 -0
  33. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +15 -0
  34. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +15 -0
  35. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +12 -0
  36. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +38 -0
  37. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +21 -0
  38. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +41 -0
  39. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +17 -0
  40. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +31 -0
  41. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +12 -0
  42. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +15 -0
  43. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +12 -0
  44. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +21 -0
  45. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +15 -0
  46. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +13 -0
  47. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +21 -0
  48. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +22 -0
  49. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
  50. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +15 -0
  51. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +40 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +76 -0
  53. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +15 -0
  54. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +22 -0
  55. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +15 -0
  56. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
  57. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +15 -0
  58. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +20 -0
  59. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +20 -0
  60. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +23 -0
  61. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +20 -0
  62. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +20 -0
  63. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +20 -0
  64. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +20 -0
  65. data/lib/active_record/connection_adapters/sqlserver/utils.rb +136 -0
  66. data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
  67. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +405 -0
  68. data/lib/active_record/connection_adapters/sqlserver_column.rb +53 -0
  69. data/lib/active_record/sqlserver_base.rb +20 -0
  70. data/lib/active_record/tasks/sqlserver_database_tasks.rb +131 -0
  71. data/lib/activerecord-sqlserver-adapter.rb +1 -0
  72. data/lib/arel/visitors/sqlserver.rb +214 -0
  73. data/lib/arel_sqlserver.rb +3 -0
  74. data/test/appveyor/dbsetup.ps1 +27 -0
  75. data/test/appveyor/dbsetup.sql +11 -0
  76. data/test/cases/adapter_test_sqlserver.rb +444 -0
  77. data/test/cases/coerced_tests.rb +713 -0
  78. data/test/cases/column_test_sqlserver.rb +780 -0
  79. data/test/cases/connection_test_sqlserver.rb +142 -0
  80. data/test/cases/execute_procedure_test_sqlserver.rb +44 -0
  81. data/test/cases/fetch_test_sqlserver.rb +57 -0
  82. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +76 -0
  83. data/test/cases/helper_sqlserver.rb +54 -0
  84. data/test/cases/migration_test_sqlserver.rb +61 -0
  85. data/test/cases/order_test_sqlserver.rb +147 -0
  86. data/test/cases/pessimistic_locking_test_sqlserver.rb +90 -0
  87. data/test/cases/rake_test_sqlserver.rb +163 -0
  88. data/test/cases/schema_dumper_test_sqlserver.rb +198 -0
  89. data/test/cases/schema_test_sqlserver.rb +54 -0
  90. data/test/cases/scratchpad_test_sqlserver.rb +9 -0
  91. data/test/cases/showplan_test_sqlserver.rb +65 -0
  92. data/test/cases/specific_schema_test_sqlserver.rb +167 -0
  93. data/test/cases/transaction_test_sqlserver.rb +66 -0
  94. data/test/cases/utils_test_sqlserver.rb +129 -0
  95. data/test/cases/uuid_test_sqlserver.rb +48 -0
  96. data/test/config.yml +41 -0
  97. data/test/debug.rb +14 -0
  98. data/test/fixtures/1px.gif +0 -0
  99. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
  100. data/test/models/sqlserver/booking.rb +3 -0
  101. data/test/models/sqlserver/customers_view.rb +3 -0
  102. data/test/models/sqlserver/datatype.rb +3 -0
  103. data/test/models/sqlserver/datatype_migration.rb +3 -0
  104. data/test/models/sqlserver/dollar_table_name.rb +3 -0
  105. data/test/models/sqlserver/dot_table_name.rb +3 -0
  106. data/test/models/sqlserver/edge_schema.rb +13 -0
  107. data/test/models/sqlserver/fk_has_fk.rb +3 -0
  108. data/test/models/sqlserver/fk_has_pk.rb +3 -0
  109. data/test/models/sqlserver/natural_pk_data.rb +4 -0
  110. data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
  111. data/test/models/sqlserver/no_pk_data.rb +3 -0
  112. data/test/models/sqlserver/object_default.rb +3 -0
  113. data/test/models/sqlserver/quoted_table.rb +7 -0
  114. data/test/models/sqlserver/quoted_view_1.rb +3 -0
  115. data/test/models/sqlserver/quoted_view_2.rb +3 -0
  116. data/test/models/sqlserver/string_default.rb +3 -0
  117. data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
  118. data/test/models/sqlserver/string_defaults_view.rb +3 -0
  119. data/test/models/sqlserver/tinyint_pk.rb +3 -0
  120. data/test/models/sqlserver/upper.rb +3 -0
  121. data/test/models/sqlserver/uppered.rb +3 -0
  122. data/test/models/sqlserver/uuid.rb +3 -0
  123. data/test/schema/datatypes/2012.sql +55 -0
  124. data/test/schema/sqlserver_specific_schema.rb +207 -0
  125. data/test/support/coerceable_test_sqlserver.rb +45 -0
  126. data/test/support/connection_reflection.rb +37 -0
  127. data/test/support/load_schema_sqlserver.rb +29 -0
  128. data/test/support/minitest_sqlserver.rb +1 -0
  129. data/test/support/paths_sqlserver.rb +50 -0
  130. data/test/support/rake_helpers.rb +41 -0
  131. data/test/support/sql_counter_sqlserver.rb +32 -0
  132. metadata +253 -0
@@ -0,0 +1,15 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module SQLServer
4
+ module Type
5
+ class UnicodeText < UnicodeVarcharMax
6
+
7
+ def type
8
+ :ntext
9
+ end
10
+
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -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,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,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