activerecord-sqlserver-adapter 3.1.1 → 3.2.18
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +253 -63
- data/VERSION +1 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +0 -7
- data/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +97 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +41 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +26 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +19 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +169 -61
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +3 -0
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +12 -4
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +85 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +102 -123
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +69 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +67 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +32 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +185 -122
- data/lib/arel/visitors/sqlserver.rb +32 -9
- metadata +46 -63
- data/README.rdoc +0 -176
- data/lib/active_record/connection_adapters/sqlserver/version.rb +0 -11
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sqlserver
|
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
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sqlserver
|
4
|
+
module CoreExt
|
5
|
+
module Relation
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def tables_in_string(string)
|
10
|
+
super - ['__rnt']
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::Relation
|
@@ -2,7 +2,9 @@ module ActiveRecord
|
|
2
2
|
module ConnectionAdapters
|
3
3
|
module Sqlserver
|
4
4
|
module DatabaseStatements
|
5
|
-
|
5
|
+
|
6
|
+
include CoreExt::DatabaseStatements
|
7
|
+
|
6
8
|
def select_rows(sql, name = nil)
|
7
9
|
raw_select sql, name, [], :fetch => :rows
|
8
10
|
end
|
@@ -14,7 +16,7 @@ module ActiveRecord
|
|
14
16
|
do_execute(sql,name)
|
15
17
|
end
|
16
18
|
end
|
17
|
-
|
19
|
+
|
18
20
|
def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {})
|
19
21
|
if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil
|
20
22
|
with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) }
|
@@ -22,11 +24,11 @@ module ActiveRecord
|
|
22
24
|
do_exec_query(sql, name, binds)
|
23
25
|
end
|
24
26
|
end
|
25
|
-
|
27
|
+
|
26
28
|
def exec_insert(sql, name, binds)
|
27
29
|
exec_query sql, name, binds, :insert => true
|
28
30
|
end
|
29
|
-
|
31
|
+
|
30
32
|
def exec_delete(sql, name, binds)
|
31
33
|
sql << "; SELECT @@ROWCOUNT AS AffectedRows"
|
32
34
|
super.rows.first.first
|
@@ -38,34 +40,42 @@ module ActiveRecord
|
|
38
40
|
end
|
39
41
|
|
40
42
|
def outside_transaction?
|
41
|
-
|
43
|
+
uncached { select_value('SELECT @@TRANCOUNT', 'SCHEMA') == 0 }
|
42
44
|
end
|
43
|
-
|
45
|
+
|
44
46
|
def supports_statement_cache?
|
45
47
|
true
|
46
48
|
end
|
47
49
|
|
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
|
+
|
48
58
|
def begin_db_transaction
|
49
59
|
do_execute "BEGIN TRANSACTION"
|
50
60
|
end
|
51
61
|
|
52
62
|
def commit_db_transaction
|
53
|
-
do_execute "COMMIT TRANSACTION"
|
63
|
+
disable_auto_reconnect { do_execute "COMMIT TRANSACTION" }
|
54
64
|
end
|
55
65
|
|
56
66
|
def rollback_db_transaction
|
57
|
-
do_execute "ROLLBACK TRANSACTION"
|
67
|
+
do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"
|
58
68
|
end
|
59
69
|
|
60
70
|
def create_savepoint
|
61
|
-
do_execute "SAVE TRANSACTION #{current_savepoint_name}"
|
71
|
+
disable_auto_reconnect { do_execute "SAVE TRANSACTION #{current_savepoint_name}" }
|
62
72
|
end
|
63
73
|
|
64
74
|
def release_savepoint
|
65
75
|
end
|
66
76
|
|
67
77
|
def rollback_to_savepoint
|
68
|
-
do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}"
|
78
|
+
disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}" }
|
69
79
|
end
|
70
80
|
|
71
81
|
def add_limit_offset!(sql, options)
|
@@ -79,11 +89,15 @@ module ActiveRecord
|
|
79
89
|
def case_sensitive_modifier(node)
|
80
90
|
node.acts_like?(:string) ? Arel::Nodes::Bin.new(node) : node
|
81
91
|
end
|
82
|
-
|
92
|
+
|
83
93
|
# === SQLServer Specific ======================================== #
|
84
|
-
|
94
|
+
|
85
95
|
def execute_procedure(proc_name, *variables)
|
86
|
-
vars = variables.
|
96
|
+
vars = if variables.any? && variables.first.is_a?(Hash)
|
97
|
+
variables.first.map { |k,v| "@#{k} = #{quote(v)}" }
|
98
|
+
else
|
99
|
+
variables.map { |v| quote(v) }
|
100
|
+
end.join(', ')
|
87
101
|
sql = "EXEC #{proc_name} #{vars}".strip
|
88
102
|
name = 'Execute Procedure'
|
89
103
|
log(sql, name) do
|
@@ -112,45 +126,130 @@ module ActiveRecord
|
|
112
126
|
end
|
113
127
|
end
|
114
128
|
end
|
115
|
-
|
129
|
+
|
116
130
|
def use_database(database=nil)
|
117
131
|
return if sqlserver_azure?
|
118
132
|
database ||= @connection_options[:database]
|
119
133
|
do_execute "USE #{quote_table_name(database)}" unless database.blank?
|
120
134
|
end
|
121
|
-
|
135
|
+
|
122
136
|
def user_options
|
123
|
-
|
124
|
-
|
125
|
-
|
137
|
+
return {} if sqlserver_azure?
|
138
|
+
# fixes #535
|
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|
|
142
|
+
if row.instance_of? Hash
|
143
|
+
set_option = row.values[0].gsub(/\s+/, '_')
|
144
|
+
user_value = row.values[1]
|
145
|
+
elsif row.instance_of? Array
|
146
|
+
set_option = row[0].gsub(/\s+/, '_')
|
126
147
|
user_value = row[1]
|
127
|
-
values[set_option] = user_value
|
128
|
-
values
|
129
148
|
end
|
149
|
+
values[set_option] = user_value
|
150
|
+
values
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def user_options_dateformat
|
155
|
+
if sqlserver_azure?
|
156
|
+
select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA'
|
157
|
+
else
|
158
|
+
user_options['dateformat']
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def user_options_isolation_level
|
163
|
+
if sqlserver_azure?
|
164
|
+
sql = %|SELECT CASE [transaction_isolation_level]
|
165
|
+
WHEN 0 THEN NULL
|
166
|
+
WHEN 1 THEN 'READ UNCOMITTED'
|
167
|
+
WHEN 2 THEN 'READ COMITTED'
|
168
|
+
WHEN 3 THEN 'REPEATABLE READ'
|
169
|
+
WHEN 4 THEN 'SERIALIZABLE'
|
170
|
+
WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level]
|
171
|
+
FROM [sys].[dm_exec_sessions]
|
172
|
+
WHERE [session_id] = @@SPID|.squish
|
173
|
+
select_value sql, 'SCHEMA'
|
174
|
+
else
|
175
|
+
user_options['isolation_level']
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def user_options_language
|
180
|
+
if sqlserver_azure?
|
181
|
+
select_value 'SELECT @@LANGUAGE AS [language]', 'SCHEMA'
|
182
|
+
else
|
183
|
+
user_options['language']
|
130
184
|
end
|
131
185
|
end
|
132
186
|
|
133
187
|
def run_with_isolation_level(isolation_level)
|
134
188
|
raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{valid_isolation_levels.to_sentence}." if !valid_isolation_levels.include?(isolation_level.upcase)
|
135
|
-
initial_isolation_level =
|
189
|
+
initial_isolation_level = user_options_isolation_level || "READ COMMITTED"
|
136
190
|
do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
|
137
191
|
begin
|
138
|
-
yield
|
192
|
+
yield
|
139
193
|
ensure
|
140
194
|
do_execute "SET TRANSACTION ISOLATION LEVEL #{initial_isolation_level}"
|
141
195
|
end if block_given?
|
142
196
|
end
|
143
|
-
|
197
|
+
|
144
198
|
def newid_function
|
145
199
|
select_value "SELECT NEWID()"
|
146
200
|
end
|
147
|
-
|
201
|
+
|
148
202
|
def newsequentialid_function
|
149
203
|
select_value "SELECT NEWSEQUENTIALID()"
|
150
204
|
end
|
151
|
-
|
205
|
+
|
206
|
+
def activity_stats
|
207
|
+
select_all %|
|
208
|
+
SELECT
|
209
|
+
[session_id] = s.session_id,
|
210
|
+
[user_process] = CONVERT(CHAR(1), s.is_user_process),
|
211
|
+
[login] = s.login_name,
|
212
|
+
[database] = ISNULL(db_name(r.database_id), N''),
|
213
|
+
[task_state] = ISNULL(t.task_state, N''),
|
214
|
+
[command] = ISNULL(r.command, N''),
|
215
|
+
[application] = ISNULL(s.program_name, N''),
|
216
|
+
[wait_time_ms] = ISNULL(w.wait_duration_ms, 0),
|
217
|
+
[wait_type] = ISNULL(w.wait_type, N''),
|
218
|
+
[wait_resource] = ISNULL(w.resource_description, N''),
|
219
|
+
[blocked_by] = ISNULL(CONVERT (varchar, w.blocking_session_id), ''),
|
220
|
+
[head_blocker] =
|
221
|
+
CASE
|
222
|
+
-- session has an active request, is blocked, but is blocking others
|
223
|
+
WHEN r2.session_id IS NOT NULL AND r.blocking_session_id = 0 THEN '1'
|
224
|
+
-- session is idle but has an open tran and is blocking others
|
225
|
+
WHEN r.session_id IS NULL THEN '1'
|
226
|
+
ELSE ''
|
227
|
+
END,
|
228
|
+
[total_cpu_ms] = s.cpu_time,
|
229
|
+
[total_physical_io_mb] = (s.reads + s.writes) * 8 / 1024,
|
230
|
+
[memory_use_kb] = s.memory_usage * 8192 / 1024,
|
231
|
+
[open_transactions] = ISNULL(r.open_transaction_count,0),
|
232
|
+
[login_time] = s.login_time,
|
233
|
+
[last_request_start_time] = s.last_request_start_time,
|
234
|
+
[host_name] = ISNULL(s.host_name, N''),
|
235
|
+
[net_address] = ISNULL(c.client_net_address, N''),
|
236
|
+
[execution_context_id] = ISNULL(t.exec_context_id, 0),
|
237
|
+
[request_id] = ISNULL(r.request_id, 0),
|
238
|
+
[workload_group] = N''
|
239
|
+
FROM sys.dm_exec_sessions s LEFT OUTER JOIN sys.dm_exec_connections c ON (s.session_id = c.session_id)
|
240
|
+
LEFT OUTER JOIN sys.dm_exec_requests r ON (s.session_id = r.session_id)
|
241
|
+
LEFT OUTER JOIN sys.dm_os_tasks t ON (r.session_id = t.session_id AND r.request_id = t.request_id)
|
242
|
+
LEFT OUTER JOIN
|
243
|
+
(SELECT *, ROW_NUMBER() OVER (PARTITION BY waiting_task_address ORDER BY wait_duration_ms DESC) AS row_num
|
244
|
+
FROM sys.dm_os_waiting_tasks
|
245
|
+
) w ON (t.task_address = w.waiting_task_address) AND w.row_num = 1
|
246
|
+
LEFT OUTER JOIN sys.dm_exec_requests r2 ON (r.session_id = r2.blocking_session_id)
|
247
|
+
WHERE db_name(r.database_id) = '#{current_database}'
|
248
|
+
ORDER BY s.session_id|
|
249
|
+
end
|
250
|
+
|
152
251
|
# === SQLServer Specific (Rake/Test Helpers) ==================== #
|
153
|
-
|
252
|
+
|
154
253
|
def recreate_database
|
155
254
|
remove_database_connections_and_rollback do
|
156
255
|
do_execute "EXEC sp_MSforeachtable 'DROP TABLE ?'"
|
@@ -194,18 +293,18 @@ module ActiveRecord
|
|
194
293
|
def current_database
|
195
294
|
select_value 'SELECT DB_NAME()'
|
196
295
|
end
|
197
|
-
|
296
|
+
|
198
297
|
def charset
|
199
298
|
select_value "SELECT SERVERPROPERTY('SqlCharSetName')"
|
200
299
|
end
|
201
|
-
|
202
|
-
|
300
|
+
|
301
|
+
|
203
302
|
protected
|
204
|
-
|
303
|
+
|
205
304
|
def select(sql, name = nil, binds = [])
|
206
305
|
exec_query(sql, name, binds).to_a
|
207
306
|
end
|
208
|
-
|
307
|
+
|
209
308
|
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
210
309
|
sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"# unless binds.empty?
|
211
310
|
super
|
@@ -214,24 +313,23 @@ module ActiveRecord
|
|
214
313
|
def last_inserted_id(result)
|
215
314
|
super || select_value("SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident")
|
216
315
|
end
|
217
|
-
|
316
|
+
|
218
317
|
# === SQLServer Specific ======================================== #
|
219
|
-
|
318
|
+
|
220
319
|
def valid_isolation_levels
|
221
320
|
["READ COMMITTED", "READ UNCOMMITTED", "REPEATABLE READ", "SERIALIZABLE", "SNAPSHOT"]
|
222
321
|
end
|
223
|
-
|
322
|
+
|
224
323
|
# === SQLServer Specific (Executing) ============================ #
|
225
324
|
|
226
|
-
def do_execute(sql, name =
|
227
|
-
name ||= 'EXECUTE'
|
325
|
+
def do_execute(sql, name = 'SQL')
|
228
326
|
log(sql, name) do
|
229
|
-
|
327
|
+
with_sqlserver_error_handling { raw_connection_do(sql) }
|
230
328
|
end
|
231
329
|
end
|
232
|
-
|
330
|
+
|
233
331
|
def do_exec_query(sql, name, binds)
|
234
|
-
|
332
|
+
explaining = name == 'EXPLAIN'
|
235
333
|
names_and_types = []
|
236
334
|
params = []
|
237
335
|
binds.each_with_index do |(column,value),index|
|
@@ -250,13 +348,20 @@ module ActiveRecord
|
|
250
348
|
raise "Unknown bind columns. We can account for this."
|
251
349
|
end
|
252
350
|
quoted_value = ar_column ? quote(v,column) : quote(v,nil)
|
253
|
-
params << "@#{index} = #{quoted_value}"
|
351
|
+
params << (explaining ? quoted_value : "@#{index} = #{quoted_value}")
|
352
|
+
end
|
353
|
+
if explaining
|
354
|
+
params.each_with_index do |param, index|
|
355
|
+
substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values.
|
356
|
+
sql.sub! substitute_at_finder, param
|
357
|
+
end
|
358
|
+
else
|
359
|
+
sql = "EXEC sp_executesql #{quote(sql)}"
|
360
|
+
sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty?
|
254
361
|
end
|
255
|
-
sql = "EXEC sp_executesql #{statement}"
|
256
|
-
sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty?
|
257
362
|
raw_select sql, name, binds, :ar_result => true
|
258
363
|
end
|
259
|
-
|
364
|
+
|
260
365
|
def raw_connection_do(sql)
|
261
366
|
case @connection_options[:mode]
|
262
367
|
when :dblib
|
@@ -267,22 +372,24 @@ module ActiveRecord
|
|
267
372
|
ensure
|
268
373
|
@update_sql = false
|
269
374
|
end
|
270
|
-
|
375
|
+
|
271
376
|
# === SQLServer Specific (Selecting) ============================ #
|
272
377
|
|
273
|
-
def raw_select(sql, name=
|
274
|
-
log(sql,name,binds)
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
378
|
+
def raw_select(sql, name='SQL', binds=[], options={})
|
379
|
+
log(sql,name,binds) { _raw_select(sql, options) }
|
380
|
+
end
|
381
|
+
|
382
|
+
def _raw_select(sql, options={})
|
383
|
+
begin
|
384
|
+
handle = raw_connection_run(sql)
|
385
|
+
handle_to_names_and_values(handle, options)
|
386
|
+
ensure
|
387
|
+
finish_statement_handle(handle)
|
281
388
|
end
|
282
389
|
end
|
283
|
-
|
390
|
+
|
284
391
|
def raw_connection_run(sql)
|
285
|
-
|
392
|
+
with_sqlserver_error_handling do
|
286
393
|
case @connection_options[:mode]
|
287
394
|
when :dblib
|
288
395
|
@connection.execute(sql)
|
@@ -291,7 +398,7 @@ module ActiveRecord
|
|
291
398
|
end
|
292
399
|
end
|
293
400
|
end
|
294
|
-
|
401
|
+
|
295
402
|
def handle_more_results?(handle)
|
296
403
|
case @connection_options[:mode]
|
297
404
|
when :dblib
|
@@ -299,7 +406,7 @@ module ActiveRecord
|
|
299
406
|
handle.more_results
|
300
407
|
end
|
301
408
|
end
|
302
|
-
|
409
|
+
|
303
410
|
def handle_to_names_and_values(handle, options={})
|
304
411
|
case @connection_options[:mode]
|
305
412
|
when :dblib
|
@@ -308,7 +415,7 @@ module ActiveRecord
|
|
308
415
|
handle_to_names_and_values_odbc(handle, options)
|
309
416
|
end
|
310
417
|
end
|
311
|
-
|
418
|
+
|
312
419
|
def handle_to_names_and_values_dblib(handle, options={})
|
313
420
|
query_options = {}.tap do |qo|
|
314
421
|
qo[:timezone] = ActiveRecord::Base.default_timezone || :utc
|
@@ -318,7 +425,7 @@ module ActiveRecord
|
|
318
425
|
columns = lowercase_schema_reflection ? handle.fields.map { |c| c.downcase } : handle.fields
|
319
426
|
options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results
|
320
427
|
end
|
321
|
-
|
428
|
+
|
322
429
|
def handle_to_names_and_values_odbc(handle, options={})
|
323
430
|
@connection.use_utc = ActiveRecord::Base.default_timezone == :utc
|
324
431
|
if options[:ar_result]
|
@@ -334,16 +441,17 @@ module ActiveRecord
|
|
334
441
|
end
|
335
442
|
end
|
336
443
|
end
|
337
|
-
|
444
|
+
|
338
445
|
def finish_statement_handle(handle)
|
339
446
|
case @connection_options[:mode]
|
340
|
-
when :dblib
|
447
|
+
when :dblib
|
448
|
+
handle.cancel if handle
|
341
449
|
when :odbc
|
342
450
|
handle.drop if handle && handle.respond_to?(:drop) && !handle.finished?
|
343
451
|
end
|
344
452
|
handle
|
345
453
|
end
|
346
|
-
|
454
|
+
|
347
455
|
end
|
348
456
|
end
|
349
457
|
end
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
10
10
|
case value
|
11
11
|
when String, ActiveSupport::Multibyte::Chars
|
12
12
|
if column && column.type == :integer && value.blank?
|
13
|
-
|
13
|
+
value.to_i.to_s
|
14
14
|
elsif column && column.type == :binary
|
15
15
|
column.class.string_to_binary(value)
|
16
16
|
elsif value.is_utf8? || (column && column.type == :string)
|
@@ -42,8 +42,7 @@ module ActiveRecord
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def quote_column_name(name)
|
45
|
-
|
46
|
-
name.to_s.split('.').map{ |n| n =~ /^\[.*\]$/ ? n : "[#{n.to_s.gsub(']', ']]')}]" }.join('.')
|
45
|
+
schema_cache.quote_name(name)
|
47
46
|
end
|
48
47
|
|
49
48
|
def quote_table_name(name)
|
@@ -68,7 +67,16 @@ module ActiveRecord
|
|
68
67
|
|
69
68
|
def quoted_datetime(value)
|
70
69
|
if value.acts_like?(:time)
|
71
|
-
|
70
|
+
time_zone_qualified_value = quoted_value_acts_like_time_filter(value)
|
71
|
+
if value.is_a?(Date)
|
72
|
+
time_zone_qualified_value.to_time.xmlschema.to(18)
|
73
|
+
else
|
74
|
+
# CHANGED [Ruby 1.8] Not needed when 1.8 is dropped.
|
75
|
+
if value.is_a?(ActiveSupport::TimeWithZone) && RUBY_VERSION < '1.9'
|
76
|
+
time_zone_qualified_value = time_zone_qualified_value.to_time
|
77
|
+
end
|
78
|
+
time_zone_qualified_value.iso8601(3).to(22)
|
79
|
+
end
|
72
80
|
else
|
73
81
|
quoted_date(value)
|
74
82
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
module Sqlserver
|
4
|
+
class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache
|
5
|
+
|
6
|
+
attr_reader :view_information
|
7
|
+
|
8
|
+
def initialize(conn)
|
9
|
+
super
|
10
|
+
@table_names = nil
|
11
|
+
@view_names = nil
|
12
|
+
@view_information = {}
|
13
|
+
@quoted_names = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
# Superclass Overrides
|
17
|
+
|
18
|
+
def table_exists?(table_name)
|
19
|
+
return false if table_name.blank?
|
20
|
+
key = table_name_key(table_name)
|
21
|
+
return @tables[key] if @tables.key? key
|
22
|
+
@tables[key] = connection.table_exists?(table_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear!
|
26
|
+
super
|
27
|
+
@table_names = nil
|
28
|
+
@view_names = nil
|
29
|
+
@view_information.clear
|
30
|
+
@quoted_names.clear
|
31
|
+
end
|
32
|
+
|
33
|
+
def clear_table_cache!(table_name)
|
34
|
+
key = table_name_key(table_name)
|
35
|
+
super(key)
|
36
|
+
super(table_name)
|
37
|
+
# SQL Server Specific
|
38
|
+
if @table_names
|
39
|
+
@table_names.delete key
|
40
|
+
@table_names.delete table_name
|
41
|
+
end
|
42
|
+
if @view_names
|
43
|
+
@view_names.delete key
|
44
|
+
@view_names.delete table_name
|
45
|
+
end
|
46
|
+
@view_information.delete key
|
47
|
+
end
|
48
|
+
|
49
|
+
# SQL Server Specific
|
50
|
+
|
51
|
+
def table_names
|
52
|
+
@table_names ||= connection.tables
|
53
|
+
end
|
54
|
+
|
55
|
+
def view_names
|
56
|
+
@view_names ||= connection.views
|
57
|
+
end
|
58
|
+
|
59
|
+
def view_exists?(table_name)
|
60
|
+
table_exists?(table_name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def view_information(table_name)
|
64
|
+
key = table_name_key(table_name)
|
65
|
+
return @view_information[key] if @view_information.key? key
|
66
|
+
@view_information[key] = connection.send(:view_information, table_name)
|
67
|
+
end
|
68
|
+
|
69
|
+
def quote_name(name)
|
70
|
+
return @quoted_names[name] if @quoted_names.key? name
|
71
|
+
@quoted_names[name] = name.to_s.split('.').map{ |n| n =~ /^\[.*\]$/ ? n : "[#{n.to_s.gsub(']', ']]')}]" }.join('.')
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def table_name_key(table_name)
|
78
|
+
Utils.unqualify_table_name(table_name)
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|