activerecord-sqlserver-adapter 3.0.19 → 3.1.0.beta1
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.
- data/CHANGELOG +2 -27
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +14 -23
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +4 -23
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +83 -89
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +11 -1
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +14 -15
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +25 -37
- data/lib/arel/visitors/sqlserver.rb +45 -32
- metadata +33 -61
- data/lib/active_record/connection_adapters/sqlserver/query_cache.rb +0 -17
data/CHANGELOG
CHANGED
@@ -1,32 +1,7 @@
|
|
1
1
|
|
2
|
-
* 3.0.
|
2
|
+
* 3.1.0.beta1 *
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
* Create a #configure_application_name method that can be overridden for unique TinyTDS app names
|
7
|
-
to show up in SQL Server's activity monitor.
|
8
|
-
|
9
|
-
* Fixed the #finish_statement_handle to cancel the TinyTDS connection if needed.
|
10
|
-
|
11
|
-
* Fix the #indexes method to fail gracefully if sp_helptext permissions fail.
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
* 3.0.18 *
|
16
|
-
|
17
|
-
* Make #rollback_db_transaction smarter.
|
18
|
-
|
19
|
-
|
20
|
-
* 3.0.17 *
|
21
|
-
|
22
|
-
* Allow :limit/:offset to be used with fully qualified table and column in :select.
|
23
|
-
|
24
|
-
* Fix test_count_explicit_columns.
|
25
|
-
|
26
|
-
|
27
|
-
* 3.0.16 *
|
28
|
-
|
29
|
-
* Allow complex order objects to not be molested by our visitor overrides. Fixes #99
|
4
|
+
It just works! Uses "EXEC sp_executesql ..." for just about everything now!
|
30
5
|
|
31
6
|
|
32
7
|
* 3.0.15 *
|
@@ -1,21 +1,25 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'active_record/base'
|
1
3
|
require 'active_record/version'
|
2
|
-
require 'active_support/
|
4
|
+
require 'active_support/concern'
|
5
|
+
require 'active_support/core_ext/class/attribute'
|
3
6
|
|
4
7
|
module ActiveRecord
|
5
8
|
module ConnectionAdapters
|
6
9
|
module Sqlserver
|
7
10
|
module CoreExt
|
8
11
|
module ActiveRecord
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
|
13
|
+
extend ActiveSupport::Concern
|
14
|
+
|
15
|
+
included do
|
16
|
+
class_attribute :coerced_sqlserver_date_columns, :coerced_sqlserver_time_columns
|
17
|
+
self.coerced_sqlserver_date_columns = Set.new
|
18
|
+
self.coerced_sqlserver_time_columns = Set.new
|
15
19
|
end
|
16
20
|
|
17
21
|
module ClassMethods
|
18
|
-
|
22
|
+
|
19
23
|
def execute_procedure(proc_name, *variables)
|
20
24
|
if connection.respond_to?(:execute_procedure)
|
21
25
|
connection.execute_procedure(proc_name,*variables)
|
@@ -25,24 +29,11 @@ module ActiveRecord
|
|
25
29
|
end
|
26
30
|
|
27
31
|
def coerce_sqlserver_date(*attributes)
|
28
|
-
|
32
|
+
self.coerced_sqlserver_date_columns += attributes.map(&:to_s)
|
29
33
|
end
|
30
34
|
|
31
35
|
def coerce_sqlserver_time(*attributes)
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
def coerced_sqlserver_date_columns
|
36
|
-
read_inheritable_attribute(:coerced_sqlserver_date_columns) || []
|
37
|
-
end
|
38
|
-
|
39
|
-
def coerced_sqlserver_time_columns
|
40
|
-
read_inheritable_attribute(:coerced_sqlserver_time_columns) || []
|
41
|
-
end
|
42
|
-
|
43
|
-
def reset_column_information_with_sqlserver_cache_support
|
44
|
-
connection.send(:initialize_sqlserver_caches) if connection.respond_to?(:sqlserver?)
|
45
|
-
reset_column_information_without_sqlserver_cache_support
|
36
|
+
self.coerced_sqlserver_time_columns += attributes.map(&:to_s)
|
46
37
|
end
|
47
38
|
|
48
39
|
end
|
@@ -4,22 +4,13 @@ module ActiveRecord
|
|
4
4
|
module CoreExt
|
5
5
|
module ODBC
|
6
6
|
|
7
|
-
module TimeStamp
|
8
|
-
|
9
|
-
def to_sqlserver_string
|
10
|
-
date, time, nanoseconds = to_s.split(' ')
|
11
|
-
"#{date} #{time}.#{sprintf("%03d",nanoseconds.to_i/1000000)}"
|
12
|
-
end
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
7
|
module Statement
|
17
8
|
|
18
9
|
def finished?
|
19
10
|
begin
|
20
11
|
connected?
|
21
12
|
false
|
22
|
-
rescue
|
13
|
+
rescue ::ODBC::Error
|
23
14
|
true
|
24
15
|
end
|
25
16
|
end
|
@@ -28,14 +19,6 @@ module ActiveRecord
|
|
28
19
|
|
29
20
|
module Database
|
30
21
|
|
31
|
-
def self.parent_modules
|
32
|
-
@parent_module ||= ['ODBC','ODBC_UTF8','ODBC_NONE'].map{ |odbc_ns| odbc_ns.constantize rescue nil }.compact
|
33
|
-
end
|
34
|
-
|
35
|
-
def self.parent_modules_error_exceptions
|
36
|
-
@parent_modules_error_exceptions ||= parent_modules.map { |odbc_ns| "::#{odbc_ns}::Error".constantize }
|
37
|
-
end
|
38
|
-
|
39
22
|
def run_block(*args)
|
40
23
|
yield sth = run(*args)
|
41
24
|
sth.drop
|
@@ -49,9 +32,7 @@ module ActiveRecord
|
|
49
32
|
end
|
50
33
|
end
|
51
34
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
ns::Database.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Database
|
56
|
-
end
|
35
|
+
|
36
|
+
ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Statement
|
37
|
+
ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Database
|
57
38
|
|
@@ -3,26 +3,47 @@ module ActiveRecord
|
|
3
3
|
module Sqlserver
|
4
4
|
module DatabaseStatements
|
5
5
|
|
6
|
-
def select_one(sql, name = nil)
|
7
|
-
result = raw_select sql, name, :fetch => :one
|
8
|
-
(result && result.first.present?) ? result.first : nil
|
9
|
-
end
|
10
|
-
|
11
6
|
def select_rows(sql, name = nil)
|
12
|
-
raw_select sql, name, :fetch => :rows
|
7
|
+
raw_select sql, name, [], :fetch => :rows
|
13
8
|
end
|
14
9
|
|
15
|
-
def execute(sql, name = nil
|
10
|
+
def execute(sql, name = nil)
|
16
11
|
if id_insert_table_name = query_requires_identity_insert?(sql)
|
17
12
|
with_identity_insert_enabled(id_insert_table_name) { do_execute(sql,name) }
|
18
13
|
else
|
19
14
|
do_execute(sql,name)
|
20
15
|
end
|
21
16
|
end
|
17
|
+
|
18
|
+
def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {})
|
19
|
+
if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil
|
20
|
+
with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) }
|
21
|
+
else
|
22
|
+
do_exec_query(sql, name, binds)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def exec_insert(sql, name, binds)
|
27
|
+
exec_query sql, name, binds, :insert => true
|
28
|
+
end
|
29
|
+
|
30
|
+
def exec_delete(sql, name, binds)
|
31
|
+
sql << "; SELECT @@ROWCOUNT AS AffectedRows"
|
32
|
+
super.rows.first.first
|
33
|
+
end
|
34
|
+
|
35
|
+
def exec_update(sql, name, binds)
|
36
|
+
sql << "; SELECT @@ROWCOUNT AS AffectedRows"
|
37
|
+
super.rows.first.first
|
38
|
+
end
|
22
39
|
|
23
40
|
def outside_transaction?
|
24
41
|
info_schema_query { select_value("SELECT @@TRANCOUNT") == 0 }
|
25
42
|
end
|
43
|
+
|
44
|
+
def supports_statement_cache?
|
45
|
+
true
|
46
|
+
end
|
26
47
|
|
27
48
|
def begin_db_transaction
|
28
49
|
do_execute "BEGIN TRANSACTION"
|
@@ -33,7 +54,7 @@ module ActiveRecord
|
|
33
54
|
end
|
34
55
|
|
35
56
|
def rollback_db_transaction
|
36
|
-
do_execute "
|
57
|
+
do_execute "ROLLBACK TRANSACTION" rescue nil
|
37
58
|
end
|
38
59
|
|
39
60
|
def create_savepoint
|
@@ -55,15 +76,8 @@ module ActiveRecord
|
|
55
76
|
"DEFAULT VALUES"
|
56
77
|
end
|
57
78
|
|
58
|
-
def
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
63
|
-
match_data = where_sql.match(/^(.*?[\]\) ])WHERE[\[\( ]/)
|
64
|
-
limit = match_data[1]
|
65
|
-
where_sql.sub!(limit,'')
|
66
|
-
"WHERE #{quoted_primary_key} IN (SELECT #{limit} #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
|
79
|
+
def case_sensitive_modifier(node)
|
80
|
+
node.acts_like?(:string) ? Arel::Nodes::Bin.new(node) : node
|
67
81
|
end
|
68
82
|
|
69
83
|
# === SQLServer Specific ======================================== #
|
@@ -190,29 +204,17 @@ module ActiveRecord
|
|
190
204
|
|
191
205
|
protected
|
192
206
|
|
193
|
-
def select(sql, name = nil)
|
194
|
-
|
207
|
+
def select(sql, name = nil, binds = [])
|
208
|
+
exec_query(sql, name, binds).to_a
|
195
209
|
end
|
196
210
|
|
197
|
-
def
|
198
|
-
|
199
|
-
|
200
|
-
when :dblib
|
201
|
-
execute(sql, name) || id_value
|
202
|
-
else
|
203
|
-
super || select_value("SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident")
|
204
|
-
end
|
211
|
+
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
212
|
+
sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"# unless binds.empty?
|
213
|
+
super
|
205
214
|
end
|
206
|
-
|
207
|
-
def
|
208
|
-
|
209
|
-
case @connection_options[:mode]
|
210
|
-
when :dblib
|
211
|
-
execute(sql, name)
|
212
|
-
else
|
213
|
-
execute(sql, name)
|
214
|
-
select_value('SELECT @@ROWCOUNT AS AffectedRows')
|
215
|
-
end
|
215
|
+
|
216
|
+
def last_inserted_id(result)
|
217
|
+
super || select_value("SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident")
|
216
218
|
end
|
217
219
|
|
218
220
|
# === SQLServer Specific ======================================== #
|
@@ -230,24 +232,50 @@ module ActiveRecord
|
|
230
232
|
end
|
231
233
|
end
|
232
234
|
|
235
|
+
def do_exec_query(sql, name, binds)
|
236
|
+
statement = quote(sql)
|
237
|
+
names_and_types = []
|
238
|
+
params = []
|
239
|
+
binds.each_with_index do |(column,value),index|
|
240
|
+
ar_column = column.is_a?(ActiveRecord::ConnectionAdapters::Column)
|
241
|
+
next if ar_column && column.sql_type == 'timestamp'
|
242
|
+
v = value
|
243
|
+
names_and_types << if ar_column
|
244
|
+
v = value.to_i if column.is_integer?
|
245
|
+
"@#{index} #{column.sql_type_for_statement}"
|
246
|
+
elsif column.acts_like?(:string)
|
247
|
+
"@#{index} nvarchar(max)"
|
248
|
+
elsif column.is_a?(Fixnum)
|
249
|
+
v = value.to_i
|
250
|
+
"@#{index} int"
|
251
|
+
else
|
252
|
+
raise "Unknown bind columns. We can account for this."
|
253
|
+
end
|
254
|
+
quoted_value = ar_column ? quote(v,column) : quote(v,nil)
|
255
|
+
params << "@#{index} = #{quoted_value}"
|
256
|
+
end
|
257
|
+
sql = "EXEC sp_executesql #{statement}"
|
258
|
+
sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty?
|
259
|
+
raw_select sql, name, binds, :ar_result => true
|
260
|
+
end
|
261
|
+
|
233
262
|
def raw_connection_do(sql)
|
234
263
|
case @connection_options[:mode]
|
235
264
|
when :dblib
|
236
|
-
@
|
265
|
+
@connection.execute(sql).do
|
237
266
|
when :odbc
|
238
267
|
@connection.do(sql)
|
239
268
|
else :adonet
|
240
269
|
@connection.create_command.tap{ |cmd| cmd.command_text = sql }.execute_non_query
|
241
270
|
end
|
242
271
|
ensure
|
243
|
-
@insert_sql = false
|
244
272
|
@update_sql = false
|
245
273
|
end
|
246
274
|
|
247
275
|
# === SQLServer Specific (Selecting) ============================ #
|
248
276
|
|
249
|
-
def raw_select(sql, name=nil, options={})
|
250
|
-
log(sql,name) do
|
277
|
+
def raw_select(sql, name=nil, binds=[], options={})
|
278
|
+
log(sql,name,binds) do
|
251
279
|
begin
|
252
280
|
handle = raw_connection_run(sql)
|
253
281
|
handle_to_names_and_values(handle, options)
|
@@ -294,56 +322,25 @@ module ActiveRecord
|
|
294
322
|
def handle_to_names_and_values_dblib(handle, options={})
|
295
323
|
query_options = {}.tap do |qo|
|
296
324
|
qo[:timezone] = ActiveRecord::Base.default_timezone || :utc
|
297
|
-
qo[:
|
298
|
-
qo[:as] = options[:fetch] == :rows ? :array : :hash
|
325
|
+
qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash
|
299
326
|
end
|
300
|
-
handle.each(query_options)
|
327
|
+
results = handle.each(query_options)
|
328
|
+
options[:ar_result] ? ActiveRecord::Result.new(handle.fields, results) : results
|
301
329
|
end
|
302
330
|
|
303
331
|
def handle_to_names_and_values_odbc(handle, options={})
|
304
|
-
@connection.use_utc = ActiveRecord::Base.default_timezone == :utc
|
305
|
-
|
306
|
-
|
307
|
-
if @connection_supports_native_types
|
308
|
-
if options[:fetch] == :all
|
309
|
-
handle.each_hash || []
|
310
|
-
else
|
311
|
-
row = handle.fetch_hash
|
312
|
-
rows = row ? [row] : [[]]
|
313
|
-
end
|
314
|
-
else
|
315
|
-
rows = if options[:fetch] == :all
|
316
|
-
handle.fetch_all || []
|
317
|
-
else
|
318
|
-
row = handle.fetch
|
319
|
-
row ? [row] : [[]]
|
320
|
-
end
|
321
|
-
names = handle.columns(true).map{ |c| c.name }
|
322
|
-
names_and_values = []
|
323
|
-
rows.each do |row|
|
324
|
-
h = {}
|
325
|
-
i = 0
|
326
|
-
while i < row.size
|
327
|
-
v = row[i]
|
328
|
-
h[names[i]] = v.respond_to?(:to_sqlserver_string) ? v.to_sqlserver_string : v
|
329
|
-
i += 1
|
330
|
-
end
|
331
|
-
names_and_values << h
|
332
|
-
end
|
333
|
-
names_and_values
|
334
|
-
end
|
335
|
-
when :rows
|
332
|
+
@connection.use_utc = ActiveRecord::Base.default_timezone == :utc
|
333
|
+
if options[:ar_result]
|
334
|
+
columns = handle.columns(true).map { |c| c.name }
|
336
335
|
rows = handle.fetch_all || []
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
end
|
336
|
+
ActiveRecord::Result.new(columns, rows)
|
337
|
+
else
|
338
|
+
case options[:fetch]
|
339
|
+
when :all
|
340
|
+
handle.each_hash || []
|
341
|
+
when :rows
|
342
|
+
handle.fetch_all || []
|
345
343
|
end
|
346
|
-
rows
|
347
344
|
end
|
348
345
|
end
|
349
346
|
|
@@ -352,7 +349,6 @@ module ActiveRecord
|
|
352
349
|
names = []
|
353
350
|
rows = []
|
354
351
|
fields_named = options[:fetch] == :rows
|
355
|
-
one_row_only = options[:fetch] == :one
|
356
352
|
while handle.read
|
357
353
|
row = []
|
358
354
|
handle.visible_field_count.times do |row_index|
|
@@ -371,7 +367,6 @@ module ActiveRecord
|
|
371
367
|
end
|
372
368
|
row << value
|
373
369
|
names << handle.get_name(row_index).to_s unless fields_named
|
374
|
-
break if one_row_only
|
375
370
|
end
|
376
371
|
rows << row
|
377
372
|
fields_named = true
|
@@ -398,8 +393,7 @@ module ActiveRecord
|
|
398
393
|
|
399
394
|
def finish_statement_handle(handle)
|
400
395
|
case @connection_options[:mode]
|
401
|
-
when :dblib
|
402
|
-
handle.cancel if handle
|
396
|
+
when :dblib
|
403
397
|
when :odbc
|
404
398
|
handle.drop if handle && handle.respond_to?(:drop) && !handle.finished?
|
405
399
|
when :adonet
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
|
10
10
|
LOST_CONNECTION_EXCEPTIONS = {
|
11
11
|
:dblib => ['TinyTds::Error'],
|
12
|
-
:odbc => ['ODBC::Error'
|
12
|
+
:odbc => ['ODBC::Error'],
|
13
13
|
:adonet => ['TypeError','System::Data::SqlClient::SqlException']
|
14
14
|
}.freeze
|
15
15
|
|
@@ -15,6 +15,8 @@ module ActiveRecord
|
|
15
15
|
else
|
16
16
|
super
|
17
17
|
end
|
18
|
+
when nil
|
19
|
+
column.respond_to?(:sql_type) && column.sql_type == 'timestamp' ? 'DEFAULT' : super
|
18
20
|
else
|
19
21
|
super
|
20
22
|
end
|
@@ -26,12 +28,20 @@ module ActiveRecord
|
|
26
28
|
|
27
29
|
def quote_column_name(name)
|
28
30
|
@sqlserver_quoted_column_and_table_names[name] ||=
|
29
|
-
name.to_s.split('.').map{ |n| n =~ /^\[.*\]$/ ? n : "[#{n
|
31
|
+
name.to_s.split('.').map{ |n| n =~ /^\[.*\]$/ ? n : "[#{n}]" }.join('.')
|
30
32
|
end
|
31
33
|
|
32
34
|
def quote_table_name(name)
|
33
35
|
quote_column_name(name)
|
34
36
|
end
|
37
|
+
|
38
|
+
def substitute_at(column, index)
|
39
|
+
if column.respond_to?(:sql_type) && column.sql_type == 'timestamp'
|
40
|
+
nil
|
41
|
+
else
|
42
|
+
Arel.sql "@#{index}"
|
43
|
+
end
|
44
|
+
end
|
35
45
|
|
36
46
|
def quoted_true
|
37
47
|
QUOTED_TRUE
|
@@ -20,8 +20,7 @@ module ActiveRecord
|
|
20
20
|
|
21
21
|
def indexes(table_name, name = nil)
|
22
22
|
unquoted_table_name = unqualify_table_name(table_name)
|
23
|
-
|
24
|
-
data.inject([]) do |indexes,index|
|
23
|
+
select("EXEC sp_helpindex #{quote_table_name(unquoted_table_name)}",name).inject([]) do |indexes,index|
|
25
24
|
index = index.with_indifferent_access
|
26
25
|
if index[:index_description] =~ /primary key/
|
27
26
|
indexes
|
@@ -40,8 +39,8 @@ module ActiveRecord
|
|
40
39
|
|
41
40
|
def columns(table_name, name = nil)
|
42
41
|
return [] if table_name.blank?
|
43
|
-
cache_key =
|
44
|
-
|
42
|
+
cache_key = unqualify_table_name(table_name)
|
43
|
+
column_definitions(table_name).collect do |ci|
|
45
44
|
sqlserver_options = ci.except(:name,:default_value,:type,:null).merge(:database_year=>database_year)
|
46
45
|
SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options
|
47
46
|
end
|
@@ -199,11 +198,13 @@ module ActiveRecord
|
|
199
198
|
ELSE 1
|
200
199
|
END as is_identity
|
201
200
|
FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS columns
|
202
|
-
WHERE columns.TABLE_NAME =
|
203
|
-
AND columns.TABLE_SCHEMA = #{table_schema.
|
201
|
+
WHERE columns.TABLE_NAME = @0
|
202
|
+
AND columns.TABLE_SCHEMA = #{table_schema.blank? ? "schema_name()" : "@1"}
|
204
203
|
ORDER BY columns.ordinal_position
|
205
204
|
}.gsub(/[ \t\r\n]+/,' ')
|
206
|
-
|
205
|
+
binds = [['table_name', table_name]]
|
206
|
+
binds << ['table_schema',table_schema] unless table_schema.blank?
|
207
|
+
results = info_schema_query { do_exec_query(sql, 'InfoSchema::ColumnDefinitions', binds) }
|
207
208
|
results.collect do |ci|
|
208
209
|
ci = ci.symbolize_keys
|
209
210
|
ci[:type] = case ci[:type]
|
@@ -279,9 +280,9 @@ module ActiveRecord
|
|
279
280
|
end
|
280
281
|
|
281
282
|
def get_table_name(sql)
|
282
|
-
if sql =~ /^\s*
|
283
|
-
$
|
284
|
-
elsif sql =~ /
|
283
|
+
if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)\s+INTO\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
|
284
|
+
$2 || $3
|
285
|
+
elsif sql =~ /FROM\s+([^\(\s]+)\s*/i
|
285
286
|
$1
|
286
287
|
else
|
287
288
|
nil
|
@@ -352,12 +353,10 @@ module ActiveRecord
|
|
352
353
|
|
353
354
|
def remove_sqlserver_columns_cache_for(table_name)
|
354
355
|
cache_key = unqualify_table_name(table_name)
|
355
|
-
@sqlserver_columns_cache[cache_key] = nil
|
356
356
|
initialize_sqlserver_caches(false)
|
357
357
|
end
|
358
358
|
|
359
359
|
def initialize_sqlserver_caches(reset_columns=true)
|
360
|
-
@sqlserver_columns_cache = {} if reset_columns
|
361
360
|
@sqlserver_views_cache = nil
|
362
361
|
@sqlserver_view_information_cache = {}
|
363
362
|
@sqlserver_quoted_column_and_table_names = {}
|
@@ -369,14 +368,14 @@ module ActiveRecord
|
|
369
368
|
if insert_sql?(sql)
|
370
369
|
table_name = get_table_name(sql)
|
371
370
|
id_column = identity_column(table_name)
|
372
|
-
id_column && sql =~ /^\s*INSERT[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false
|
371
|
+
id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false
|
373
372
|
else
|
374
373
|
false
|
375
374
|
end
|
376
375
|
end
|
377
376
|
|
378
377
|
def insert_sql?(sql)
|
379
|
-
!(sql =~ /^\s*INSERT/i).nil?
|
378
|
+
!(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil?
|
380
379
|
end
|
381
380
|
|
382
381
|
def with_identity_insert_enabled(table_name)
|
@@ -389,7 +388,7 @@ module ActiveRecord
|
|
389
388
|
|
390
389
|
def set_identity_insert(table_name, enable = true)
|
391
390
|
sql = "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
|
392
|
-
do_execute
|
391
|
+
do_execute sql,'InfoSchema::SetIdentityInsert'
|
393
392
|
rescue Exception => e
|
394
393
|
raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
|
395
394
|
end
|
@@ -5,7 +5,6 @@ require 'active_record/connection_adapters/sqlserver/core_ext/active_record'
|
|
5
5
|
require 'active_record/connection_adapters/sqlserver/database_limits'
|
6
6
|
require 'active_record/connection_adapters/sqlserver/database_statements'
|
7
7
|
require 'active_record/connection_adapters/sqlserver/errors'
|
8
|
-
require 'active_record/connection_adapters/sqlserver/query_cache'
|
9
8
|
require 'active_record/connection_adapters/sqlserver/schema_statements'
|
10
9
|
require 'active_record/connection_adapters/sqlserver/quoting'
|
11
10
|
require 'active_support/core_ext/kernel/requires'
|
@@ -26,16 +25,7 @@ module ActiveRecord
|
|
26
25
|
warn("TinyTds v0.4.3 or higher required. Using #{TinyTds::VERSION}") unless TinyTds::Client.instance_methods.map(&:to_s).include?("active?")
|
27
26
|
when :odbc
|
28
27
|
raise ArgumentError, 'Missing :dsn configuration.' unless config.has_key?(:dsn)
|
29
|
-
|
30
|
-
require_library_or_gem 'odbc'
|
31
|
-
else
|
32
|
-
begin
|
33
|
-
# TODO: [ODBC] Change this to 'odbc_utf8'
|
34
|
-
require_library_or_gem 'odbc'
|
35
|
-
rescue LoadError
|
36
|
-
require_library_or_gem 'odbc'
|
37
|
-
end
|
38
|
-
end unless ['::ODBC','::ODBC_UTF8','::ODBC_NONE'].any? { |odbc_ns| odbc_ns.constantize rescue nil }
|
28
|
+
require_library_or_gem 'odbc'
|
39
29
|
require 'active_record/connection_adapters/sqlserver/core_ext/odbc'
|
40
30
|
when :adonet
|
41
31
|
require 'System.Data'
|
@@ -61,7 +51,7 @@ module ActiveRecord
|
|
61
51
|
module ConnectionAdapters
|
62
52
|
|
63
53
|
class SQLServerColumn < Column
|
64
|
-
|
54
|
+
|
65
55
|
def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {})
|
66
56
|
@sqlserver_options = sqlserver_options.symbolize_keys
|
67
57
|
super(name, default, sql_type, null)
|
@@ -87,6 +77,14 @@ module ActiveRecord
|
|
87
77
|
@sql_type =~ /nvarchar|ntext|nchar/i
|
88
78
|
end
|
89
79
|
|
80
|
+
def is_integer?
|
81
|
+
@sql_type =~ /int/i
|
82
|
+
end
|
83
|
+
|
84
|
+
def sql_type_for_statement
|
85
|
+
is_integer? ? sql_type.sub(/\(\d+\)/,'') : sql_type
|
86
|
+
end
|
87
|
+
|
90
88
|
def default_function
|
91
89
|
@sqlserver_options[:default_function]
|
92
90
|
end
|
@@ -160,11 +158,10 @@ module ActiveRecord
|
|
160
158
|
include Sqlserver::DatabaseStatements
|
161
159
|
include Sqlserver::SchemaStatements
|
162
160
|
include Sqlserver::DatabaseLimits
|
163
|
-
include Sqlserver::QueryCache
|
164
161
|
include Sqlserver::Errors
|
165
162
|
|
166
163
|
ADAPTER_NAME = 'SQLServer'.freeze
|
167
|
-
VERSION = '3.0.
|
164
|
+
VERSION = '3.1.0.beta1'.freeze
|
168
165
|
DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/
|
169
166
|
SUPPORTED_VERSIONS = [2005,2008,2010,2011].freeze
|
170
167
|
|
@@ -265,6 +262,11 @@ module ActiveRecord
|
|
265
262
|
remove_database_connections_and_rollback { }
|
266
263
|
end
|
267
264
|
|
265
|
+
def clear_cache!
|
266
|
+
# This requires db admin perms and I'm not even sure it is a good idea.
|
267
|
+
# raw_connection_do "DBCC FREEPROCCACHE WITH NO_INFOMSGS" rescue nil
|
268
|
+
end
|
269
|
+
|
268
270
|
# === Abstract Adapter (Misc Support) =========================== #
|
269
271
|
|
270
272
|
def pk_and_sequence_for(table_name)
|
@@ -331,7 +333,7 @@ module ActiveRecord
|
|
331
333
|
end
|
332
334
|
|
333
335
|
def cs_equality_operator
|
334
|
-
@@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS
|
336
|
+
@@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS'
|
335
337
|
end
|
336
338
|
|
337
339
|
|
@@ -358,7 +360,7 @@ module ActiveRecord
|
|
358
360
|
config = @connection_options
|
359
361
|
@connection = case @connection_options[:mode]
|
360
362
|
when :dblib
|
361
|
-
appname = config[:appname] ||
|
363
|
+
appname = config[:appname] || Rails.application.class.name.split('::').first rescue nil
|
362
364
|
login_timeout = config[:login_timeout].present? ? config[:login_timeout].to_i : nil
|
363
365
|
timeout = config[:timeout].present? ? config[:timeout].to_i/1000 : nil
|
364
366
|
encoding = config[:encoding].present? ? config[:encoding] : nil
|
@@ -390,20 +392,20 @@ module ActiveRecord
|
|
390
392
|
end
|
391
393
|
end
|
392
394
|
when :odbc
|
393
|
-
odbc = ['::ODBC','::ODBC_UTF8','::ODBC_NONE'].detect{ |odbc_ns| odbc_ns.constantize rescue nil }.constantize
|
394
395
|
if config[:dsn].include?(';')
|
395
|
-
driver =
|
396
|
+
driver = ODBC::Driver.new.tap do |d|
|
396
397
|
d.name = config[:dsn_name] || 'Driver1'
|
397
398
|
d.attrs = config[:dsn].split(';').map{ |atr| atr.split('=') }.reject{ |kv| kv.size != 2 }.inject({}){ |h,kv| k,v = kv ; h[k] = v ; h }
|
398
399
|
end
|
399
|
-
|
400
|
+
ODBC::Database.new.drvconnect(driver)
|
400
401
|
else
|
401
|
-
|
402
|
-
end.tap do |c|
|
403
|
-
|
402
|
+
ODBC.connect config[:dsn], config[:username], config[:password]
|
403
|
+
end.tap do |c|
|
404
|
+
begin
|
404
405
|
c.use_time = true
|
405
406
|
c.use_utc = ActiveRecord::Base.default_timezone == :utc
|
406
|
-
|
407
|
+
rescue Exception => e
|
408
|
+
warn "Ruby ODBC v0.99992 or higher is required."
|
407
409
|
end
|
408
410
|
end
|
409
411
|
when :adonet
|
@@ -423,24 +425,10 @@ module ActiveRecord
|
|
423
425
|
connection.open
|
424
426
|
end
|
425
427
|
end
|
426
|
-
configure_connection
|
427
428
|
rescue
|
428
429
|
raise unless @auto_connecting
|
429
430
|
end
|
430
431
|
|
431
|
-
# Override this method so every connection can be configured to your needs.
|
432
|
-
# For example:
|
433
|
-
# do_execute "SET TEXTSIZE #{64.megabytes}"
|
434
|
-
# do_execute "SET CONCAT_NULL_YIELDS_NULL ON"
|
435
|
-
def configure_connection
|
436
|
-
end
|
437
|
-
|
438
|
-
# Override this method so every connection can have a unique name. Max 30 characters. Used by TinyTDS only.
|
439
|
-
# For example:
|
440
|
-
# "myapp_#{$$}_#{Thread.current.object_id}".to(29)
|
441
|
-
def configure_application_name
|
442
|
-
end
|
443
|
-
|
444
432
|
def remove_database_connections_and_rollback(database=nil)
|
445
433
|
database ||= current_database
|
446
434
|
do_execute "ALTER DATABASE #{quote_table_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
|
@@ -4,10 +4,6 @@ module Arel
|
|
4
4
|
|
5
5
|
module Nodes
|
6
6
|
|
7
|
-
# See the SelectManager#lock method on why this custom class is needed.
|
8
|
-
class LockWithSQLServer < Arel::Nodes::Unary
|
9
|
-
end
|
10
|
-
|
11
7
|
# Extending the Ordering class to be comparrison friendly which allows us to call #uniq on a
|
12
8
|
# collection of them. See SelectManager#order for more details.
|
13
9
|
class Ordering < Arel::Nodes::Binary
|
@@ -26,8 +22,6 @@ module Arel
|
|
26
22
|
|
27
23
|
class SelectManager < Arel::TreeManager
|
28
24
|
|
29
|
-
alias :lock_without_sqlserver :lock
|
30
|
-
|
31
25
|
# Getting real Ordering objects is very important for us. We need to be able to call #uniq on
|
32
26
|
# a colleciton of them reliably as well as using their true object attributes to mutate them
|
33
27
|
# to grouping objects for the inner sql during a select statment with an offset/rownumber. So this
|
@@ -41,8 +35,6 @@ module Arel
|
|
41
35
|
table = Arel::Table.new(x.relation.table_alias || x.relation.name)
|
42
36
|
expr = table[x.name]
|
43
37
|
Arel::Nodes::Ordering.new expr
|
44
|
-
when Arel::Nodes::Ordering
|
45
|
-
x
|
46
38
|
when String
|
47
39
|
x.split(',').map do |s|
|
48
40
|
expr, direction = s.split
|
@@ -60,9 +52,17 @@ module Arel
|
|
60
52
|
|
61
53
|
# A friendly over ride that allows us to put a special lock object that can have a default or pass
|
62
54
|
# custom string hints down. See the visit_Arel_Nodes_LockWithSQLServer delegation method.
|
55
|
+
alias :lock_without_sqlserver :lock
|
63
56
|
def lock(locking=true)
|
64
57
|
if Arel::Visitors::SQLServer === @visitor
|
65
|
-
|
58
|
+
case locking
|
59
|
+
when true
|
60
|
+
locking = Arel.sql('WITH(HOLDLOCK, ROWLOCK)')
|
61
|
+
when Arel::Nodes::SqlLiteral
|
62
|
+
when String
|
63
|
+
locking = Arel.sql locking
|
64
|
+
end
|
65
|
+
@ast.lock = Arel::Nodes::Lock.new(locking)
|
66
66
|
self
|
67
67
|
else
|
68
68
|
lock_without_sqlserver(locking)
|
@@ -96,22 +96,14 @@ module Arel
|
|
96
96
|
"TOP (#{visit o.expr})"
|
97
97
|
end
|
98
98
|
|
99
|
-
def visit_Arel_Nodes_Lock
|
100
|
-
|
99
|
+
def visit_Arel_Nodes_Lock(o)
|
100
|
+
visit o.expr
|
101
101
|
end
|
102
102
|
|
103
|
-
def
|
104
|
-
|
105
|
-
when TrueClass
|
106
|
-
"WITH(HOLDLOCK, ROWLOCK)"
|
107
|
-
when String
|
108
|
-
o.expr
|
109
|
-
else
|
110
|
-
""
|
111
|
-
end
|
103
|
+
def visit_Arel_Nodes_Bin(o)
|
104
|
+
"#{visit o.expr} #{@engine.connection.cs_equality_operator}"
|
112
105
|
end
|
113
106
|
|
114
|
-
|
115
107
|
# SQLServer ToSql/Visitor (Additions)
|
116
108
|
|
117
109
|
def visit_Arel_Nodes_SelectStatementWithOutOffset(o, windowed=false)
|
@@ -129,6 +121,8 @@ module Arel
|
|
129
121
|
expr = Arel.sql projection_without_expression(x.expr)
|
130
122
|
x.descending? ? Arel::Nodes::Max.new([expr]) : Arel::Nodes::Min.new([expr])
|
131
123
|
end
|
124
|
+
elsif top_one_everything_for_through_join?(o)
|
125
|
+
projections = projections.map { |x| projection_without_expression(x) }
|
132
126
|
end
|
133
127
|
[ ("SELECT" if !windowed),
|
134
128
|
(visit(o.limit) if o.limit && !windowed),
|
@@ -178,9 +172,8 @@ module Arel
|
|
178
172
|
# SQLServer Helpers
|
179
173
|
|
180
174
|
def source_with_lock_for_select_statement(o)
|
181
|
-
# TODO: [ARel 2.2] Use #from/#source vs. #froms
|
182
175
|
core = o.cores.first
|
183
|
-
source =
|
176
|
+
source = visit(core.source).strip if core.source
|
184
177
|
if source && o.lock
|
185
178
|
lock = visit o.lock
|
186
179
|
index = source.match(/FROM [\w\[\]\.]+/)[0].length
|
@@ -220,11 +213,21 @@ module Arel
|
|
220
213
|
((p1.respond_to?(:distinct) && p1.distinct) ||
|
221
214
|
p1.respond_to?(:include?) && p1.include?('DISTINCT'))
|
222
215
|
end
|
216
|
+
|
217
|
+
def single_distinct_select_everything_statement?(o)
|
218
|
+
single_distinct_select_statement?(o) && visit(o.cores.first.projections.first).ends_with?(".*")
|
219
|
+
end
|
220
|
+
|
221
|
+
def top_one_everything_for_through_join?(o)
|
222
|
+
single_distinct_select_everything_statement?(o) &&
|
223
|
+
(o.limit && !o.offset) &&
|
224
|
+
join_in_select_statement?(o)
|
225
|
+
end
|
223
226
|
|
224
227
|
def all_projections_aliased_in_select_statement?(o)
|
225
228
|
projections = o.cores.first.projections
|
226
229
|
projections.all? do |x|
|
227
|
-
x.split(',').all? { |y| y.include?(' AS ') }
|
230
|
+
visit(x).split(',').all? { |y| y.include?(' AS ') }
|
228
231
|
end
|
229
232
|
end
|
230
233
|
|
@@ -235,14 +238,15 @@ module Arel
|
|
235
238
|
|
236
239
|
def eager_limiting_select_statement?(o)
|
237
240
|
core = o.cores.first
|
238
|
-
single_distinct_select_statement?(o) &&
|
241
|
+
single_distinct_select_statement?(o) &&
|
242
|
+
(o.limit && !o.offset) &&
|
243
|
+
core.groups.empty? &&
|
244
|
+
!single_distinct_select_everything_statement?(o)
|
239
245
|
end
|
240
246
|
|
241
247
|
def join_in_select_statement?(o)
|
242
248
|
core = o.cores.first
|
243
|
-
|
244
|
-
# core.source.right.any? { |x| Arel::Nodes::Join === x }
|
245
|
-
Arel::Nodes::Join === core.froms
|
249
|
+
core.source.right.any? { |x| Arel::Nodes::Join === x }
|
246
250
|
end
|
247
251
|
|
248
252
|
def complex_count_sql?(o)
|
@@ -291,11 +295,20 @@ module Arel
|
|
291
295
|
end
|
292
296
|
elsif join_in_select_statement?(o) && all_projections_aliased_in_select_statement?(o)
|
293
297
|
core.projections.map do |x|
|
294
|
-
Arel.sql x.split(',').map{ |y| y.split(' AS ').last.strip }.join(', ')
|
298
|
+
Arel.sql visit(x).split(',').map{ |y| y.split(' AS ').last.strip }.join(', ')
|
295
299
|
end
|
300
|
+
elsif function_select_statement?(o)
|
301
|
+
[Arel.star]
|
296
302
|
else
|
297
|
-
|
298
|
-
|
303
|
+
core.projections.map do |x|
|
304
|
+
if x.respond_to?(:relation)
|
305
|
+
x = x.dup
|
306
|
+
x.relation = x.relation.dup
|
307
|
+
x.relation.instance_variable_set :@table_alias, Arel.sql('[__rnt].*')
|
308
|
+
else
|
309
|
+
x
|
310
|
+
end
|
311
|
+
end
|
299
312
|
end
|
300
313
|
end
|
301
314
|
|
@@ -312,7 +325,7 @@ module Arel
|
|
312
325
|
|
313
326
|
# TODO: We use this for grouping too, maybe make Grouping objects vs SqlLiteral.
|
314
327
|
def projection_without_expression(projection)
|
315
|
-
Arel.sql(projection.split(',').map do |x|
|
328
|
+
Arel.sql(visit(projection).split(',').map do |x|
|
316
329
|
x.strip!
|
317
330
|
x.sub!(/^(COUNT|SUM|MAX|MIN|AVG)\s*(\((.*)\))?/,'\3')
|
318
331
|
x.sub!(/^DISTINCT\s*/,'')
|
metadata
CHANGED
@@ -1,15 +1,10 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-sqlserver-adapter
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 3
|
8
|
-
- 0
|
9
|
-
- 19
|
10
|
-
version: 3.0.19
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 3.1.0.beta1
|
5
|
+
prerelease: 6
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Ken Collins
|
14
9
|
- Murray Steele
|
15
10
|
- Shawn Balestracci
|
@@ -18,51 +13,38 @@ authors:
|
|
18
13
|
autorequire:
|
19
14
|
bindir: bin
|
20
15
|
cert_chain: []
|
21
|
-
|
22
|
-
date: 2011-10-23 00:00:00 -04:00
|
16
|
+
date: 2011-05-06 00:00:00.000000000 -04:00
|
23
17
|
default_executable:
|
24
|
-
dependencies:
|
25
|
-
- !ruby/object:Gem::Dependency
|
18
|
+
dependencies:
|
19
|
+
- !ruby/object:Gem::Dependency
|
26
20
|
name: activerecord
|
27
|
-
|
28
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
21
|
+
requirement: &2171145400 !ruby/object:Gem::Requirement
|
29
22
|
none: false
|
30
|
-
requirements:
|
23
|
+
requirements:
|
31
24
|
- - ~>
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
hash: 1
|
34
|
-
segments:
|
35
|
-
- 3
|
36
|
-
- 0
|
37
|
-
- 3
|
25
|
+
- !ruby/object:Gem::Version
|
38
26
|
version: 3.0.3
|
39
27
|
type: :runtime
|
40
|
-
version_requirements: *id001
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: arel
|
43
28
|
prerelease: false
|
44
|
-
|
29
|
+
version_requirements: *2171145400
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: arel
|
32
|
+
requirement: &2171144680 !ruby/object:Gem::Requirement
|
45
33
|
none: false
|
46
|
-
requirements:
|
34
|
+
requirements:
|
47
35
|
- - ~>
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
hash: 1
|
50
|
-
segments:
|
51
|
-
- 2
|
52
|
-
- 0
|
53
|
-
- 7
|
36
|
+
- !ruby/object:Gem::Version
|
54
37
|
version: 2.0.7
|
55
38
|
type: :runtime
|
56
|
-
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: *2171144680
|
57
41
|
description: SQL Server 2005 and 2008 Adapter For ActiveRecord
|
58
42
|
email: ken@metaskills.net
|
59
43
|
executables: []
|
60
|
-
|
61
44
|
extensions: []
|
62
|
-
|
63
|
-
extra_rdoc_files:
|
45
|
+
extra_rdoc_files:
|
64
46
|
- README.rdoc
|
65
|
-
files:
|
47
|
+
files:
|
66
48
|
- CHANGELOG
|
67
49
|
- MIT-LICENSE
|
68
50
|
- README.rdoc
|
@@ -71,7 +53,6 @@ files:
|
|
71
53
|
- lib/active_record/connection_adapters/sqlserver/database_limits.rb
|
72
54
|
- lib/active_record/connection_adapters/sqlserver/database_statements.rb
|
73
55
|
- lib/active_record/connection_adapters/sqlserver/errors.rb
|
74
|
-
- lib/active_record/connection_adapters/sqlserver/query_cache.rb
|
75
56
|
- lib/active_record/connection_adapters/sqlserver/quoting.rb
|
76
57
|
- lib/active_record/connection_adapters/sqlserver/schema_statements.rb
|
77
58
|
- lib/active_record/connection_adapters/sqlserver_adapter.rb
|
@@ -80,37 +61,28 @@ files:
|
|
80
61
|
has_rdoc: true
|
81
62
|
homepage: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter
|
82
63
|
licenses: []
|
83
|
-
|
84
64
|
post_install_message:
|
85
|
-
rdoc_options:
|
65
|
+
rdoc_options:
|
86
66
|
- --main
|
87
67
|
- README.rdoc
|
88
|
-
require_paths:
|
68
|
+
require_paths:
|
89
69
|
- lib
|
90
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
71
|
none: false
|
92
|
-
requirements:
|
93
|
-
- -
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
|
96
|
-
|
97
|
-
- 0
|
98
|
-
version: "0"
|
99
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
77
|
none: false
|
101
|
-
requirements:
|
102
|
-
- -
|
103
|
-
- !ruby/object:Gem::Version
|
104
|
-
|
105
|
-
segments:
|
106
|
-
- 0
|
107
|
-
version: "0"
|
78
|
+
requirements:
|
79
|
+
- - ! '>'
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: 1.3.1
|
108
82
|
requirements: []
|
109
|
-
|
110
83
|
rubyforge_project: activerecord-sqlserver-adapter
|
111
84
|
rubygems_version: 1.6.2
|
112
85
|
signing_key:
|
113
86
|
specification_version: 3
|
114
87
|
summary: SQL Server 2005 and 2008 Adapter For ActiveRecord.
|
115
88
|
test_files: []
|
116
|
-
|