activerecord-sqlserver-adapter 3.2.18 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|