activerecord-sqlserver-adapter 3.2.18 → 4.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 +4 -4
- data/CHANGELOG +4 -28
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -7
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +6 -9
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +3 -25
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +4 -14
- data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +1 -3
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +2 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +74 -80
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +10 -14
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +24 -15
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +24 -19
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +28 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +118 -77
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +10 -13
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +8 -11
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +2 -5
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +23 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +4 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +121 -247
- data/lib/active_record/connection_adapters/sqlserver_column.rb +116 -0
- data/lib/active_record/sqlserver_base.rb +28 -0
- data/lib/active_record/sqlserver_test_case.rb +17 -0
- data/lib/arel/arel_sqlserver.rb +5 -0
- data/lib/arel/nodes_sqlserver.rb +14 -0
- data/lib/arel/select_manager_sqlserver.rb +62 -0
- data/lib/arel/visitors/sqlserver.rb +251 -188
- metadata +32 -10
- data/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +0 -97
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e47a3b956b52f24e1dc9f743d5f91590e1fb4337
|
4
|
+
data.tar.gz: f4a3544a7539acb775ff6cfa75925f1cf615e4c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24465af624f8c38d423bd545842e007eaf86371ea802f8259ed54e6eb5b18ddafaf884b95e93637fc608f2033533415d6ca69663f6489afcaf4d04f8518e0250
|
7
|
+
data.tar.gz: 0029cf9f6b4f6de57f61848473ef243234cd32bce09f594e09a87bca8208c88626f06bed6bd186310d4f8b4749e6b03a32e0c88eeb7e6858767d8843518a9708
|
data/CHANGELOG
CHANGED
@@ -1,31 +1,7 @@
|
|
1
|
-
*
|
2
|
-
|
3
|
-
*
|
4
|
-
|
5
|
-
* 3.2.17 *
|
6
|
-
|
7
|
-
* Add `WITH NO_INFOMSGS` to `user_options` method. Fixes #580
|
8
|
-
|
9
|
-
|
10
|
-
* 3.2.16 *
|
11
|
-
|
12
|
-
* All user optons to be array/hash. Fixes #540
|
13
|
-
|
14
|
-
|
15
|
-
* 3.2.15 *
|
16
|
-
|
17
|
-
* Added vNext support.
|
18
|
-
|
19
|
-
|
20
|
-
* 3.2.14 *
|
21
|
-
|
22
|
-
* Added 2016 to supported list.
|
23
|
-
|
24
|
-
|
25
|
-
* 3.2.13 *
|
26
|
-
|
27
|
-
* Allow 2014 to be used.
|
28
|
-
|
1
|
+
* 4.0.0 *
|
2
|
+
* Dropped support for ruby 1.8.7
|
3
|
+
* Removed deadlock victim retry in favor of Isolation Level
|
4
|
+
* Removed auto_explain_threshold_in_seconds (not used in rails 4)
|
29
5
|
|
30
6
|
* 3.2.12 *
|
31
7
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
4.0.0
|
@@ -3,9 +3,8 @@ module ActiveRecord
|
|
3
3
|
module Sqlserver
|
4
4
|
module CoreExt
|
5
5
|
module ActiveRecord
|
6
|
-
|
7
6
|
extend ActiveSupport::Concern
|
8
|
-
|
7
|
+
|
9
8
|
included do
|
10
9
|
class_attribute :coerced_sqlserver_date_columns, :coerced_sqlserver_time_columns
|
11
10
|
self.coerced_sqlserver_date_columns = Set.new
|
@@ -13,10 +12,9 @@ module ActiveRecord
|
|
13
12
|
end
|
14
13
|
|
15
14
|
module ClassMethods
|
16
|
-
|
17
15
|
def execute_procedure(proc_name, *variables)
|
18
16
|
if connection.respond_to?(:execute_procedure)
|
19
|
-
connection.execute_procedure(proc_name
|
17
|
+
connection.execute_procedure(proc_name, *variables)
|
20
18
|
else
|
21
19
|
[]
|
22
20
|
end
|
@@ -29,14 +27,11 @@ module ActiveRecord
|
|
29
27
|
def coerce_sqlserver_time(*attributes)
|
30
28
|
self.coerced_sqlserver_time_columns += attributes.map(&:to_s)
|
31
29
|
end
|
32
|
-
|
33
30
|
end
|
34
|
-
|
35
31
|
end
|
36
32
|
end
|
37
33
|
end
|
38
34
|
end
|
39
35
|
end
|
40
36
|
|
41
|
-
|
42
37
|
ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ActiveRecord
|
@@ -3,19 +3,18 @@ module ActiveRecord
|
|
3
3
|
module Sqlserver
|
4
4
|
module CoreExt
|
5
5
|
module Explain
|
6
|
-
|
7
|
-
SQLSERVER_STATEMENT_PREFIX = "EXEC sp_executesql "
|
6
|
+
SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql '
|
8
7
|
SQLSERVER_PARAM_MATCHER = /@\d+ =/
|
9
|
-
|
8
|
+
|
10
9
|
def exec_explain(queries)
|
11
10
|
unprepared_queries = queries.map { |sql, bind| [unprepare_sqlserver_statement(sql), bind] }
|
12
11
|
super(unprepared_queries)
|
13
12
|
end
|
14
|
-
|
13
|
+
|
15
14
|
private
|
16
|
-
|
17
|
-
# This is somewhat hacky, but it should reliably reformat our prepared sql statment
|
18
|
-
# which uses sp_executesql to just the first argument, then unquote it. Likewise our
|
15
|
+
|
16
|
+
# This is somewhat hacky, but it should reliably reformat our prepared sql statment
|
17
|
+
# which uses sp_executesql to just the first argument, then unquote it. Likewise our
|
19
18
|
# do_exec_query method should substitude the @n args withe the quoted values.
|
20
19
|
def unprepare_sqlserver_statement(sql)
|
21
20
|
if sql.starts_with?(SQLSERVER_STATEMENT_PREFIX)
|
@@ -29,8 +28,6 @@ module ActiveRecord
|
|
29
28
|
sql
|
30
29
|
end
|
31
30
|
end
|
32
|
-
|
33
|
-
|
34
31
|
end
|
35
32
|
end
|
36
33
|
end
|
@@ -1,26 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
module CoreExt
|
5
|
-
class ExplainSubscriber
|
6
|
-
def call(*args)
|
7
|
-
if queries = Thread.current[:available_queries_for_explain]
|
8
|
-
payload = args.last
|
9
|
-
queries << payload.values_at(:sql, :binds) unless ignore_sqlserver_payload?(payload)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
|
14
|
-
SQLSERVER_EXPLAINED_SQLS = /(select|update|delete|insert)/i
|
15
|
-
|
16
|
-
# Need to modify the regex for the TSQL generated by this adapter so we can explain the proper sql statements
|
17
|
-
def ignore_sqlserver_payload?(payload)
|
18
|
-
payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ SQLSERVER_EXPLAINED_SQLS
|
19
|
-
end
|
20
|
-
|
21
|
-
ActiveSupport::Notifications.subscribe("sql.active_record", new)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
1
|
+
silence_warnings do
|
2
|
+
# Already defined in Rails
|
3
|
+
ActiveRecord::ExplainSubscriber::EXPLAINED_SQLS = /(select|update|delete|insert)\b/i
|
26
4
|
end
|
@@ -3,36 +3,26 @@ module ActiveRecord
|
|
3
3
|
module Sqlserver
|
4
4
|
module CoreExt
|
5
5
|
module ODBC
|
6
|
-
|
7
6
|
module Statement
|
8
|
-
|
9
7
|
def finished?
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
true
|
15
|
-
end
|
8
|
+
connected?
|
9
|
+
false
|
10
|
+
rescue ::ODBC::Error
|
11
|
+
true
|
16
12
|
end
|
17
|
-
|
18
13
|
end
|
19
14
|
|
20
15
|
module Database
|
21
|
-
|
22
16
|
def run_block(*args)
|
23
17
|
yield sth = run(*args)
|
24
18
|
sth.drop
|
25
19
|
end
|
26
|
-
|
27
20
|
end
|
28
|
-
|
29
21
|
end
|
30
22
|
end
|
31
23
|
end
|
32
24
|
end
|
33
25
|
end
|
34
26
|
|
35
|
-
|
36
27
|
ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Statement
|
37
28
|
ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Database
|
38
|
-
|
@@ -2,7 +2,6 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
3
3
|
module Sqlserver
|
4
4
|
module DatabaseLimits
|
5
|
-
|
6
5
|
def table_alias_length
|
7
6
|
128
|
8
7
|
end
|
@@ -32,17 +31,16 @@ module ActiveRecord
|
|
32
31
|
end
|
33
32
|
|
34
33
|
def in_clause_length
|
35
|
-
|
34
|
+
65_536
|
36
35
|
end
|
37
36
|
|
38
37
|
def sql_query_length
|
39
|
-
|
38
|
+
65_536 * 4_096
|
40
39
|
end
|
41
40
|
|
42
41
|
def joins_per_query
|
43
42
|
256
|
44
43
|
end
|
45
|
-
|
46
44
|
end
|
47
45
|
end
|
48
46
|
end
|
@@ -2,69 +2,58 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
3
3
|
module Sqlserver
|
4
4
|
module DatabaseStatements
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def select_rows(sql, name = nil)
|
9
|
-
raw_select sql, name, [], :fetch => :rows
|
5
|
+
def select_rows(sql, name = nil, binds = [])
|
6
|
+
do_exec_query sql, name, binds, fetch: :rows
|
10
7
|
end
|
11
8
|
|
12
9
|
def execute(sql, name = nil)
|
13
10
|
if id_insert_table_name = query_requires_identity_insert?(sql)
|
14
|
-
with_identity_insert_enabled(id_insert_table_name) { do_execute(sql,name) }
|
11
|
+
with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) }
|
15
12
|
else
|
16
|
-
do_execute(sql,name)
|
13
|
+
do_execute(sql, name)
|
17
14
|
end
|
18
15
|
end
|
19
16
|
|
20
17
|
def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {})
|
21
18
|
if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil
|
22
19
|
with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) }
|
20
|
+
elsif update_sql?(sql)
|
21
|
+
sql = strip_ident_from_update(sql)
|
22
|
+
do_exec_query(sql, name, binds)
|
23
23
|
else
|
24
24
|
do_exec_query(sql, name, binds)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
# The abstract adapter ignores the last two parameters also
|
29
|
+
def exec_insert(sql, name, binds, _pk = nil, _sequence_name = nil)
|
30
|
+
exec_query sql, name, binds, insert: true
|
30
31
|
end
|
31
32
|
|
32
33
|
def exec_delete(sql, name, binds)
|
33
|
-
sql <<
|
34
|
+
sql << '; SELECT @@ROWCOUNT AS AffectedRows'
|
34
35
|
super.rows.first.first
|
35
36
|
end
|
36
37
|
|
37
38
|
def exec_update(sql, name, binds)
|
38
|
-
sql <<
|
39
|
+
sql << '; SELECT @@ROWCOUNT AS AffectedRows'
|
39
40
|
super.rows.first.first
|
40
41
|
end
|
41
42
|
|
42
|
-
def outside_transaction?
|
43
|
-
uncached { select_value('SELECT @@TRANCOUNT', 'SCHEMA') == 0 }
|
44
|
-
end
|
45
|
-
|
46
43
|
def supports_statement_cache?
|
47
44
|
true
|
48
45
|
end
|
49
46
|
|
50
|
-
def transaction(options = {})
|
51
|
-
if retry_deadlock_victim?
|
52
|
-
block_given? ? transaction_with_retry_deadlock_victim(options) { yield } : transaction_with_retry_deadlock_victim(options)
|
53
|
-
else
|
54
|
-
block_given? ? super(options) { yield } : super(options)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
47
|
def begin_db_transaction
|
59
|
-
do_execute
|
48
|
+
do_execute 'BEGIN TRANSACTION'
|
60
49
|
end
|
61
50
|
|
62
51
|
def commit_db_transaction
|
63
|
-
disable_auto_reconnect { do_execute
|
52
|
+
disable_auto_reconnect { do_execute 'COMMIT TRANSACTION' }
|
64
53
|
end
|
65
54
|
|
66
55
|
def rollback_db_transaction
|
67
|
-
do_execute
|
56
|
+
do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
|
68
57
|
end
|
69
58
|
|
70
59
|
def create_savepoint
|
@@ -78,12 +67,12 @@ module ActiveRecord
|
|
78
67
|
disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}" }
|
79
68
|
end
|
80
69
|
|
81
|
-
def add_limit_offset!(
|
70
|
+
def add_limit_offset!(_sql, _options)
|
82
71
|
raise NotImplementedError, 'This has been moved to the SQLServerCompiler in Arel.'
|
83
72
|
end
|
84
73
|
|
85
74
|
def empty_insert_statement_value
|
86
|
-
|
75
|
+
'DEFAULT VALUES'
|
87
76
|
end
|
88
77
|
|
89
78
|
def case_sensitive_modifier(node)
|
@@ -94,7 +83,7 @@ module ActiveRecord
|
|
94
83
|
|
95
84
|
def execute_procedure(proc_name, *variables)
|
96
85
|
vars = if variables.any? && variables.first.is_a?(Hash)
|
97
|
-
variables.first.map { |k,v| "@#{k} = #{quote(v)}" }
|
86
|
+
variables.first.map { |k, v| "@#{k} = #{quote(v)}" }
|
98
87
|
else
|
99
88
|
variables.map { |v| quote(v) }
|
100
89
|
end.join(', ')
|
@@ -104,41 +93,36 @@ module ActiveRecord
|
|
104
93
|
case @connection_options[:mode]
|
105
94
|
when :dblib
|
106
95
|
result = @connection.execute(sql)
|
107
|
-
result.each(:
|
96
|
+
result.each(as: :hash, cache_rows: true) do |row|
|
108
97
|
r = row.with_indifferent_access
|
109
98
|
yield(r) if block_given?
|
110
99
|
end
|
111
|
-
result.each.map{ |row| row.is_a?(Hash) ? row.with_indifferent_access : row }
|
100
|
+
result.each.map { |row| row.is_a?(Hash) ? row.with_indifferent_access : row }
|
112
101
|
when :odbc
|
113
102
|
results = []
|
114
103
|
raw_connection_run(sql) do |handle|
|
115
|
-
get_rows = lambda
|
116
|
-
rows = handle_to_names_and_values handle, :
|
117
|
-
rows.each_with_index { |r,i| rows[i] = r.with_indifferent_access }
|
104
|
+
get_rows = lambda do
|
105
|
+
rows = handle_to_names_and_values handle, fetch: :all
|
106
|
+
rows.each_with_index { |r, i| rows[i] = r.with_indifferent_access }
|
118
107
|
results << rows
|
119
|
-
}
|
120
|
-
get_rows.call
|
121
|
-
while handle_more_results?(handle)
|
122
|
-
get_rows.call
|
123
108
|
end
|
109
|
+
get_rows.call
|
110
|
+
get_rows.call while handle_more_results?(handle)
|
124
111
|
end
|
125
112
|
results.many? ? results : results.first
|
126
113
|
end
|
127
114
|
end
|
128
115
|
end
|
129
116
|
|
130
|
-
def use_database(database=nil)
|
117
|
+
def use_database(database = nil)
|
131
118
|
return if sqlserver_azure?
|
132
119
|
database ||= @connection_options[:database]
|
133
|
-
do_execute "USE #{
|
120
|
+
do_execute "USE #{quote_database_name(database)}" unless database.blank?
|
134
121
|
end
|
135
122
|
|
136
123
|
def user_options
|
137
124
|
return {} if sqlserver_azure?
|
138
|
-
|
139
|
-
rows = select_rows('DBCC USEROPTIONS WITH NO_INFOMSGS', 'SCHEMA')
|
140
|
-
rows = rows.first if rows.size == 2 && rows.last.empty?
|
141
|
-
rows.reduce(HashWithIndifferentAccess.new) do |values, row|
|
125
|
+
select_rows('dbcc useroptions', 'SCHEMA').reduce(HashWithIndifferentAccess.new) do |values, row|
|
142
126
|
if row.instance_of? Hash
|
143
127
|
set_option = row.values[0].gsub(/\s+/, '_')
|
144
128
|
user_value = row.values[1]
|
@@ -151,6 +135,7 @@ module ActiveRecord
|
|
151
135
|
end
|
152
136
|
end
|
153
137
|
|
138
|
+
# TODO: Rails 4 now supports isolation levels
|
154
139
|
def user_options_dateformat
|
155
140
|
if sqlserver_azure?
|
156
141
|
select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA'
|
@@ -161,7 +146,7 @@ module ActiveRecord
|
|
161
146
|
|
162
147
|
def user_options_isolation_level
|
163
148
|
if sqlserver_azure?
|
164
|
-
sql =
|
149
|
+
sql = %(SELECT CASE [transaction_isolation_level]
|
165
150
|
WHEN 0 THEN NULL
|
166
151
|
WHEN 1 THEN 'READ UNCOMITTED'
|
167
152
|
WHEN 2 THEN 'READ COMITTED'
|
@@ -169,7 +154,7 @@ module ActiveRecord
|
|
169
154
|
WHEN 4 THEN 'SERIALIZABLE'
|
170
155
|
WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level]
|
171
156
|
FROM [sys].[dm_exec_sessions]
|
172
|
-
WHERE [session_id] = @@SPID
|
157
|
+
WHERE [session_id] = @@SPID).squish
|
173
158
|
select_value sql, 'SCHEMA'
|
174
159
|
else
|
175
160
|
user_options['isolation_level']
|
@@ -185,8 +170,10 @@ module ActiveRecord
|
|
185
170
|
end
|
186
171
|
|
187
172
|
def run_with_isolation_level(isolation_level)
|
188
|
-
|
189
|
-
|
173
|
+
unless valid_isolation_levels.include?(isolation_level.upcase)
|
174
|
+
raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{valid_isolation_levels.to_sentence}."
|
175
|
+
end
|
176
|
+
initial_isolation_level = user_options_isolation_level || 'READ COMMITTED'
|
190
177
|
do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
|
191
178
|
begin
|
192
179
|
yield
|
@@ -196,11 +183,11 @@ module ActiveRecord
|
|
196
183
|
end
|
197
184
|
|
198
185
|
def newid_function
|
199
|
-
select_value
|
186
|
+
select_value 'SELECT NEWID()'
|
200
187
|
end
|
201
188
|
|
202
189
|
def newsequentialid_function
|
203
|
-
select_value
|
190
|
+
select_value 'SELECT NEWSEQUENTIALID()'
|
204
191
|
end
|
205
192
|
|
206
193
|
def activity_stats
|
@@ -256,7 +243,7 @@ module ActiveRecord
|
|
256
243
|
end
|
257
244
|
end
|
258
245
|
|
259
|
-
def recreate_database!(database=nil)
|
246
|
+
def recreate_database!(database = nil)
|
260
247
|
current_db = current_database
|
261
248
|
database ||= current_db
|
262
249
|
this_db = database.to_s == current_db
|
@@ -271,7 +258,7 @@ module ActiveRecord
|
|
271
258
|
retry_count = 0
|
272
259
|
max_retries = 1
|
273
260
|
begin
|
274
|
-
do_execute "DROP DATABASE #{
|
261
|
+
do_execute "DROP DATABASE #{quote_database_name(database)}"
|
275
262
|
rescue ActiveRecord::StatementInvalid => err
|
276
263
|
if err.message =~ /because it is currently in use/i
|
277
264
|
raise if retry_count >= max_retries
|
@@ -286,8 +273,12 @@ module ActiveRecord
|
|
286
273
|
end
|
287
274
|
end
|
288
275
|
|
289
|
-
def create_database(database)
|
290
|
-
|
276
|
+
def create_database(database, collation = @connection_options[:collation])
|
277
|
+
if collation
|
278
|
+
do_execute "CREATE DATABASE #{quote_database_name(database)} COLLATE #{collation}"
|
279
|
+
else
|
280
|
+
do_execute "CREATE DATABASE #{quote_database_name(database)}"
|
281
|
+
end
|
291
282
|
end
|
292
283
|
|
293
284
|
def current_database
|
@@ -298,26 +289,26 @@ module ActiveRecord
|
|
298
289
|
select_value "SELECT SERVERPROPERTY('SqlCharSetName')"
|
299
290
|
end
|
300
291
|
|
301
|
-
|
302
292
|
protected
|
303
293
|
|
304
294
|
def select(sql, name = nil, binds = [])
|
305
|
-
exec_query(sql, name, binds)
|
295
|
+
exec_query(sql, name, binds)
|
306
296
|
end
|
307
297
|
|
308
298
|
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
309
|
-
sql =
|
299
|
+
sql =
|
300
|
+
if pk
|
301
|
+
sql.insert(sql.index(/ (DEFAULT )?VALUES/), " OUTPUT inserted.#{pk}")
|
302
|
+
else
|
303
|
+
"#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
|
304
|
+
end
|
310
305
|
super
|
311
306
|
end
|
312
307
|
|
313
|
-
def last_inserted_id(result)
|
314
|
-
super || select_value("SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident")
|
315
|
-
end
|
316
|
-
|
317
308
|
# === SQLServer Specific ======================================== #
|
318
309
|
|
319
310
|
def valid_isolation_levels
|
320
|
-
[
|
311
|
+
['READ COMMITTED', 'READ UNCOMMITTED', 'REPEATABLE READ', 'SERIALIZABLE', 'SNAPSHOT']
|
321
312
|
end
|
322
313
|
|
323
314
|
# === SQLServer Specific (Executing) ============================ #
|
@@ -328,11 +319,17 @@ module ActiveRecord
|
|
328
319
|
end
|
329
320
|
end
|
330
321
|
|
331
|
-
def do_exec_query(sql, name, binds)
|
322
|
+
def do_exec_query(sql, name, binds, options = {})
|
323
|
+
# This allows non-AR code to utilize the binds
|
324
|
+
# handling code, e.g. select_rows()
|
325
|
+
if options[:fetch] != :rows
|
326
|
+
options[:ar_result] = true
|
327
|
+
end
|
328
|
+
|
332
329
|
explaining = name == 'EXPLAIN'
|
333
330
|
names_and_types = []
|
334
331
|
params = []
|
335
|
-
binds.each_with_index do |(column,value),index|
|
332
|
+
binds.each_with_index do |(column, value), index|
|
336
333
|
ar_column = column.is_a?(ActiveRecord::ConnectionAdapters::Column)
|
337
334
|
next if ar_column && column.sql_type == 'timestamp'
|
338
335
|
v = value
|
@@ -345,9 +342,9 @@ module ActiveRecord
|
|
345
342
|
v = value.to_i
|
346
343
|
"@#{index} int"
|
347
344
|
else
|
348
|
-
raise
|
345
|
+
raise 'Unknown bind columns. We can account for this.'
|
349
346
|
end
|
350
|
-
quoted_value = ar_column ? quote(v,column) : quote(v,nil)
|
347
|
+
quoted_value = ar_column ? quote(v, column) : quote(v, nil)
|
351
348
|
params << (explaining ? quoted_value : "@#{index} = #{quoted_value}")
|
352
349
|
end
|
353
350
|
if explaining
|
@@ -359,7 +356,7 @@ module ActiveRecord
|
|
359
356
|
sql = "EXEC sp_executesql #{quote(sql)}"
|
360
357
|
sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty?
|
361
358
|
end
|
362
|
-
raw_select sql, name, binds,
|
359
|
+
raw_select sql, name, binds, options
|
363
360
|
end
|
364
361
|
|
365
362
|
def raw_connection_do(sql)
|
@@ -375,17 +372,15 @@ module ActiveRecord
|
|
375
372
|
|
376
373
|
# === SQLServer Specific (Selecting) ============================ #
|
377
374
|
|
378
|
-
def raw_select(sql, name='SQL', binds=[], options={})
|
379
|
-
log(sql,name,binds) { _raw_select(sql, options) }
|
375
|
+
def raw_select(sql, name = 'SQL', binds = [], options = {})
|
376
|
+
log(sql, name, binds) { _raw_select(sql, options) }
|
380
377
|
end
|
381
378
|
|
382
|
-
def _raw_select(sql, options={})
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
finish_statement_handle(handle)
|
388
|
-
end
|
379
|
+
def _raw_select(sql, options = {})
|
380
|
+
handle = raw_connection_run(sql)
|
381
|
+
handle_to_names_and_values(handle, options)
|
382
|
+
ensure
|
383
|
+
finish_statement_handle(handle)
|
389
384
|
end
|
390
385
|
|
391
386
|
def raw_connection_run(sql)
|
@@ -407,7 +402,7 @@ module ActiveRecord
|
|
407
402
|
end
|
408
403
|
end
|
409
404
|
|
410
|
-
def handle_to_names_and_values(handle, options={})
|
405
|
+
def handle_to_names_and_values(handle, options = {})
|
411
406
|
case @connection_options[:mode]
|
412
407
|
when :dblib
|
413
408
|
handle_to_names_and_values_dblib(handle, options)
|
@@ -416,7 +411,7 @@ module ActiveRecord
|
|
416
411
|
end
|
417
412
|
end
|
418
413
|
|
419
|
-
def handle_to_names_and_values_dblib(handle, options={})
|
414
|
+
def handle_to_names_and_values_dblib(handle, options = {})
|
420
415
|
query_options = {}.tap do |qo|
|
421
416
|
qo[:timezone] = ActiveRecord::Base.default_timezone || :utc
|
422
417
|
qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash
|
@@ -426,7 +421,7 @@ module ActiveRecord
|
|
426
421
|
options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results
|
427
422
|
end
|
428
423
|
|
429
|
-
def handle_to_names_and_values_odbc(handle, options={})
|
424
|
+
def handle_to_names_and_values_odbc(handle, options = {})
|
430
425
|
@connection.use_utc = ActiveRecord::Base.default_timezone == :utc
|
431
426
|
if options[:ar_result]
|
432
427
|
columns = lowercase_schema_reflection ? handle.columns(true).map { |c| c.name.downcase } : handle.columns(true).map { |c| c.name }
|
@@ -451,7 +446,6 @@ module ActiveRecord
|
|
451
446
|
end
|
452
447
|
handle
|
453
448
|
end
|
454
|
-
|
455
449
|
end
|
456
450
|
end
|
457
451
|
end
|