rails-sqlserver-2000-2005-adapter 2.2.15 → 2.2.16
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 +19 -1
- data/README.rdoc +8 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +105 -21
- data/test/cases/adapter_test_sqlserver.rb +74 -0
- data/test/cases/connection_test_sqlserver.rb +39 -0
- data/test/cases/offset_and_limit_test_sqlserver.rb +20 -1
- data/test/cases/specific_schema_test_sqlserver.rb +31 -0
- data/test/schema/sqlserver_specific_schema.rb +4 -0
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,7 +1,25 @@
|
|
1
1
|
|
2
2
|
MASTER
|
3
3
|
|
4
|
-
*
|
4
|
+
*
|
5
|
+
|
6
|
+
|
7
|
+
* 2.2.16 * (April 21st, 2009)
|
8
|
+
|
9
|
+
* Make add_limit_offset! only add locking hints (for tally) when the :lock option is present. Added tests
|
10
|
+
to make sure tally SQL is augmented correctly and tests to make sure that add_lock! is doing what it needs
|
11
|
+
for deep sub selects in paginated results. [Ken Collins]
|
12
|
+
|
13
|
+
* Add auto reconnect support utilizing a new #with_auto_reconnect block. By default each query run through
|
14
|
+
the adapter will automatically reconnect at standard intervals, logging attempts along the way, till success
|
15
|
+
or the original exception bubbles up. See docs for more details. Resolves ticket #18 [Ken Collins]
|
16
|
+
|
17
|
+
* Update internal helper method #orders_and_dirs_set to cope with an order clause like "description desc". This
|
18
|
+
resolves ticket #26 [Ken Collins]
|
19
|
+
|
20
|
+
* Provide support for running queries at different isolation levels using #run_with_isolation_level method
|
21
|
+
that can take a block or not. Also implement a #user_options method that reflects on the current user
|
22
|
+
session values. Resolves #20 [Murray Steele]
|
5
23
|
|
6
24
|
|
7
25
|
* 2.2.15 * (March 23rd, 2009)
|
data/README.rdoc
CHANGED
@@ -16,6 +16,8 @@ The SQL Server adapter for rails is back for ActiveRecord 2.2 and up! We are cur
|
|
16
16
|
* Enabled #case_sensitive_equality_operator used by unique validations.
|
17
17
|
* Unicode character support for nchar, nvarchar and ntext data types. Configuration option for defaulting all string data types to the unicode safe types.
|
18
18
|
* View support for table names, identity inserts, and column defaults.
|
19
|
+
* A block method to run queries within a specific isolation level.
|
20
|
+
* Automatically reconnects to lost database connections.
|
19
21
|
|
20
22
|
==== Date/Time Data Type Hinting
|
21
23
|
|
@@ -103,6 +105,12 @@ By default all queries to the INFORMATION_SCHEMA table is silenced. If you think
|
|
103
105
|
|
104
106
|
ActiveRecord::ConnectionAdapters::SQLServerAdapter.log_info_schema_queries = true
|
105
107
|
|
108
|
+
==== Auto Connecting
|
109
|
+
|
110
|
+
By default the adapter will auto connect to lost DB connections. For every query it will retry at intervals of 2, 4, 8, 16 and 32 seconds. During each retry it will callback out to ActiveRecord::Base.did_retry_sqlserver_connection(connection,count). When all retries fail, it will callback to ActiveRecord::Base.did_lose_sqlserver_connection(connection). Both implementations of these methods are to write to the rails logger, however, they make great override points for notifications like Hoptoad. If you want to disable automatic reconnections use the following in an initializer.
|
111
|
+
|
112
|
+
ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = false
|
113
|
+
|
106
114
|
|
107
115
|
== Versions
|
108
116
|
|
@@ -23,9 +23,17 @@ module ActiveRecord
|
|
23
23
|
host = config[:host] ? config[:host].to_s : 'localhost'
|
24
24
|
driver_url = "DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User ID=#{username};Password=#{password};"
|
25
25
|
end
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
ConnectionAdapters::SQLServerAdapter.new(logger, [driver_url, username, password])
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def self.did_retry_sqlserver_connection(connection,count)
|
32
|
+
logger.info "CONNECTION RETRY: #{connection.class.name} retry ##{count}."
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.did_lose_sqlserver_connection(connection)
|
36
|
+
logger.info "CONNECTION LOST: #{connection.class.name}"
|
29
37
|
end
|
30
38
|
|
31
39
|
end
|
@@ -150,13 +158,20 @@ module ActiveRecord
|
|
150
158
|
class SQLServerAdapter < AbstractAdapter
|
151
159
|
|
152
160
|
ADAPTER_NAME = 'SQLServer'.freeze
|
153
|
-
VERSION = '2.2.
|
161
|
+
VERSION = '2.2.16'.freeze
|
154
162
|
DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+(\d{4})/
|
155
163
|
SUPPORTED_VERSIONS = [2000,2005].freeze
|
156
164
|
LIMITABLE_TYPES = ['string','integer','float','char','nchar','varchar','nvarchar'].freeze
|
157
165
|
|
166
|
+
LOST_CONNECTION_EXCEPTIONS = [DBI::DatabaseError, DBI::InterfaceError]
|
167
|
+
LOST_CONNECTION_MESSAGES = [
|
168
|
+
'Communication link failure',
|
169
|
+
'Read from the server failed',
|
170
|
+
'Write to the server failed',
|
171
|
+
'Database connection was already closed']
|
172
|
+
|
158
173
|
cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type,
|
159
|
-
:log_info_schema_queries, :enable_default_unicode_types
|
174
|
+
:log_info_schema_queries, :enable_default_unicode_types, :auto_connect
|
160
175
|
|
161
176
|
class << self
|
162
177
|
|
@@ -166,9 +181,10 @@ module ActiveRecord
|
|
166
181
|
|
167
182
|
end
|
168
183
|
|
169
|
-
def initialize(
|
170
|
-
super(connection, logger)
|
184
|
+
def initialize(logger, connection_options)
|
171
185
|
@connection_options = connection_options
|
186
|
+
connect
|
187
|
+
super(raw_connection, logger)
|
172
188
|
initialize_sqlserver_caches
|
173
189
|
unless SUPPORTED_VERSIONS.include?(database_year)
|
174
190
|
raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported."
|
@@ -221,6 +237,10 @@ module ActiveRecord
|
|
221
237
|
"#<#{self.class} version: #{version}, year: #{database_year}, connection_options: #{@connection_options.inspect}>"
|
222
238
|
end
|
223
239
|
|
240
|
+
def auto_connect
|
241
|
+
@@auto_connect.is_a?(FalseClass) ? false : true
|
242
|
+
end
|
243
|
+
|
224
244
|
def native_string_database_type
|
225
245
|
@@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar')
|
226
246
|
end
|
@@ -302,16 +322,14 @@ module ActiveRecord
|
|
302
322
|
def active?
|
303
323
|
raw_connection.execute("SELECT 1").finish
|
304
324
|
true
|
305
|
-
rescue
|
325
|
+
rescue *LOST_CONNECTION_EXCEPTIONS
|
306
326
|
false
|
307
327
|
end
|
308
328
|
|
309
329
|
def reconnect!
|
310
330
|
disconnect!
|
311
|
-
|
312
|
-
|
313
|
-
@logger.warn "#{adapter_name} reconnection failed: #{e.message}" if @logger
|
314
|
-
false
|
331
|
+
connect
|
332
|
+
active?
|
315
333
|
end
|
316
334
|
|
317
335
|
def disconnect!
|
@@ -325,6 +343,30 @@ module ActiveRecord
|
|
325
343
|
|
326
344
|
# DATABASE STATEMENTS ======================================#
|
327
345
|
|
346
|
+
def user_options
|
347
|
+
info_schema_query do
|
348
|
+
select_rows("dbcc useroptions").inject(HashWithIndifferentAccess.new) do |values,row|
|
349
|
+
set_option = row[0].gsub(/\s+/,'_')
|
350
|
+
user_value = row[1]
|
351
|
+
values[set_option] = user_value
|
352
|
+
values
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
VALID_ISOLATION_LEVELS = ["READ COMMITTED", "READ UNCOMMITTED", "REPEATABLE READ", "SERIALIZABLE", "SNAPSHOT"]
|
358
|
+
|
359
|
+
def run_with_isolation_level(isolation_level)
|
360
|
+
raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{VALID_ISOLATION_LEVELS.to_sentence}." if !VALID_ISOLATION_LEVELS.include?(isolation_level.upcase)
|
361
|
+
initial_isolation_level = user_options[:isolation_level] || "READ COMMITTED"
|
362
|
+
do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
|
363
|
+
begin
|
364
|
+
yield
|
365
|
+
ensure
|
366
|
+
do_execute "SET TRANSACTION ISOLATION LEVEL #{initial_isolation_level}"
|
367
|
+
end if block_given?
|
368
|
+
end
|
369
|
+
|
328
370
|
def select_rows(sql, name = nil)
|
329
371
|
raw_select(sql,name).last
|
330
372
|
end
|
@@ -395,7 +437,7 @@ module ActiveRecord
|
|
395
437
|
# The business of adding limit/offset
|
396
438
|
if options[:limit] and options[:offset]
|
397
439
|
tally_sql = "SELECT count(*) as TotalRows from (#{sql.sub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally"
|
398
|
-
add_lock! tally_sql,
|
440
|
+
add_lock! tally_sql, options
|
399
441
|
total_rows = select_value(tally_sql).to_i
|
400
442
|
if (options[:limit] + options[:offset]) >= total_rows
|
401
443
|
options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
|
@@ -697,6 +739,47 @@ module ActiveRecord
|
|
697
739
|
|
698
740
|
protected
|
699
741
|
|
742
|
+
# CONNECTION MANAGEMENT ====================================#
|
743
|
+
|
744
|
+
def connect
|
745
|
+
driver_url, username, password = @connection_options
|
746
|
+
@connection = DBI.connect(driver_url, username, password)
|
747
|
+
configure_connection
|
748
|
+
rescue
|
749
|
+
raise unless @auto_connecting
|
750
|
+
end
|
751
|
+
|
752
|
+
def configure_connection
|
753
|
+
raw_connection['AutoCommit'] = true
|
754
|
+
end
|
755
|
+
|
756
|
+
def with_auto_reconnect
|
757
|
+
begin
|
758
|
+
yield
|
759
|
+
rescue *LOST_CONNECTION_EXCEPTIONS => e
|
760
|
+
if LOST_CONNECTION_MESSAGES.any? { |lcm| e.message =~ Regexp.new(lcm,Regexp::IGNORECASE) }
|
761
|
+
retry if auto_reconnected?
|
762
|
+
end
|
763
|
+
raise
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
def auto_reconnected?
|
768
|
+
return false unless auto_connect
|
769
|
+
@auto_connecting = true
|
770
|
+
count = 0
|
771
|
+
while count <= 5
|
772
|
+
sleep 2** count
|
773
|
+
ActiveRecord::Base.did_retry_sqlserver_connection(self,count)
|
774
|
+
return true if reconnect!
|
775
|
+
count += 1
|
776
|
+
end
|
777
|
+
ActiveRecord::Base.did_lose_sqlserver_connection(self)
|
778
|
+
false
|
779
|
+
ensure
|
780
|
+
@auto_connecting = false
|
781
|
+
end
|
782
|
+
|
700
783
|
# DATABASE STATEMENTS ======================================
|
701
784
|
|
702
785
|
def select(sql, name = nil, ignore_special_columns = false)
|
@@ -727,9 +810,9 @@ module ActiveRecord
|
|
727
810
|
def raw_execute(sql, name = nil, &block)
|
728
811
|
log(sql, name) do
|
729
812
|
if block_given?
|
730
|
-
raw_connection.execute(sql) { |handle| yield(handle) }
|
813
|
+
with_auto_reconnect { raw_connection.execute(sql) { |handle| yield(handle) } }
|
731
814
|
else
|
732
|
-
raw_connection.execute(sql)
|
815
|
+
with_auto_reconnect { raw_connection.execute(sql) }
|
733
816
|
end
|
734
817
|
end
|
735
818
|
end
|
@@ -743,7 +826,7 @@ module ActiveRecord
|
|
743
826
|
|
744
827
|
def do_execute(sql,name=nil)
|
745
828
|
log(sql, name || 'EXECUTE') do
|
746
|
-
raw_connection.do(sql)
|
829
|
+
with_auto_reconnect { raw_connection.do(sql) }
|
747
830
|
end
|
748
831
|
end
|
749
832
|
|
@@ -877,12 +960,13 @@ module ActiveRecord
|
|
877
960
|
orders = order.sub('ORDER BY','').split(',').map(&:strip).reject(&:blank?)
|
878
961
|
orders_dirs = orders.map do |ord|
|
879
962
|
dir = nil
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
963
|
+
ord.sub!(/\b(asc|desc)$/i) do |match|
|
964
|
+
if match
|
965
|
+
dir = match.upcase.strip
|
966
|
+
''
|
967
|
+
end
|
884
968
|
end
|
885
|
-
[ord,dir]
|
969
|
+
[ord.strip, dir]
|
886
970
|
end
|
887
971
|
end
|
888
972
|
|
@@ -5,6 +5,8 @@ require 'models/joke'
|
|
5
5
|
require 'models/subscriber'
|
6
6
|
|
7
7
|
class AdapterTestSqlserver < ActiveRecord::TestCase
|
8
|
+
|
9
|
+
fixtures :tasks
|
8
10
|
|
9
11
|
def setup
|
10
12
|
@connection = ActiveRecord::Base.connection
|
@@ -376,6 +378,78 @@ class AdapterTestSqlserver < ActiveRecord::TestCase
|
|
376
378
|
|
377
379
|
context 'For DatabaseStatements' do
|
378
380
|
|
381
|
+
context "finding out what user_options are available" do
|
382
|
+
|
383
|
+
should "run the database consistency checker useroptions command" do
|
384
|
+
@connection.expects(:select_rows).with(regexp_matches(/^dbcc\s+useroptions$/i)).returns []
|
385
|
+
@connection.user_options
|
386
|
+
end
|
387
|
+
|
388
|
+
should "return a underscored key hash with indifferent access of the results" do
|
389
|
+
@connection.expects(:select_rows).with(regexp_matches(/^dbcc\s+useroptions$/i)).returns [['some', 'thing'], ['isolation level', 'read uncommitted']]
|
390
|
+
uo = @connection.user_options
|
391
|
+
assert_equal 2, uo.keys.size
|
392
|
+
assert_equal 'thing', uo['some']
|
393
|
+
assert_equal 'thing', uo[:some]
|
394
|
+
assert_equal 'read uncommitted', uo['isolation_level']
|
395
|
+
assert_equal 'read uncommitted', uo[:isolation_level]
|
396
|
+
end
|
397
|
+
|
398
|
+
end
|
399
|
+
|
400
|
+
context "altering isolation levels" do
|
401
|
+
|
402
|
+
should "barf if the requested isolation level is not valid" do
|
403
|
+
assert_raise(ArgumentError) do
|
404
|
+
@connection.run_with_isolation_level 'INVALID ISOLATION LEVEL' do; end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
context "with a valid isolation level" do
|
409
|
+
|
410
|
+
setup do
|
411
|
+
@t1 = tasks(:first_task)
|
412
|
+
@t2 = tasks(:another_task)
|
413
|
+
assert @t1, 'Tasks :first_task should be in AR fixtures'
|
414
|
+
assert @t2, 'Tasks :another_task should be in AR fixtures'
|
415
|
+
good_isolation_level = @connection.user_options[:isolation_level].blank? || @connection.user_options[:isolation_level] =~ /read committed/i
|
416
|
+
assert good_isolation_level, "User isolation level is not at a happy starting place: #{@connection.user_options[:isolation_level].inspect}"
|
417
|
+
end
|
418
|
+
|
419
|
+
should 'allow #run_with_isolation_level to not take a block to set it' do
|
420
|
+
begin
|
421
|
+
@connection.run_with_isolation_level 'READ UNCOMMITTED'
|
422
|
+
assert_match %r|read uncommitted|i, @connection.user_options[:isolation_level]
|
423
|
+
ensure
|
424
|
+
@connection.run_with_isolation_level 'READ COMMITTED'
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
428
|
+
should 'return block value using #run_with_isolation_level' do
|
429
|
+
assert_same_elements Task.find(:all), @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(:all) }
|
430
|
+
end
|
431
|
+
|
432
|
+
should 'pass a read uncommitted isolation level test' do
|
433
|
+
assert_nil @t2.starting, 'Fixture should have this empty.'
|
434
|
+
begin
|
435
|
+
Task.transaction do
|
436
|
+
@t2.starting = Time.now
|
437
|
+
@t2.save
|
438
|
+
@dirty_t2 = @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(@t2.id) }
|
439
|
+
raise ActiveRecord::ActiveRecordError
|
440
|
+
end
|
441
|
+
rescue
|
442
|
+
'Do Nothing'
|
443
|
+
end
|
444
|
+
assert @dirty_t2, 'Should have a Task record from within block above.'
|
445
|
+
assert @dirty_t2.starting, 'Should have a dirty date.'
|
446
|
+
assert_nil Task.find(@t2.id).starting, 'Should be nil again from botched transaction above.'
|
447
|
+
end unless active_record_2_point_2? # Transactions in tests are a bit screwy in 2.2.
|
448
|
+
|
449
|
+
end
|
450
|
+
|
451
|
+
end
|
452
|
+
|
379
453
|
end
|
380
454
|
|
381
455
|
context 'For SchemaStatements' do
|
@@ -78,6 +78,37 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase
|
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
|
+
context 'Connection management' do
|
82
|
+
|
83
|
+
setup do
|
84
|
+
assert @connection.active?
|
85
|
+
end
|
86
|
+
|
87
|
+
should 'be able to disconnect and reconnect at will' do
|
88
|
+
@connection.disconnect!
|
89
|
+
assert !@connection.active?
|
90
|
+
@connection.reconnect!
|
91
|
+
assert @connection.active?
|
92
|
+
end
|
93
|
+
|
94
|
+
should 'auto reconnect when setting is on' do
|
95
|
+
with_auto_connect(true) do
|
96
|
+
@connection.disconnect!
|
97
|
+
assert_nothing_raised() { Topic.count }
|
98
|
+
assert @connection.active?
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
should 'not auto reconnect when setting is off' do
|
103
|
+
with_auto_connect(false) do
|
104
|
+
@connection.disconnect!
|
105
|
+
assert_raise(ActiveRecord::StatementInvalid) { Topic.count }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
|
81
112
|
|
82
113
|
private
|
83
114
|
|
@@ -99,5 +130,13 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase
|
|
99
130
|
ensure
|
100
131
|
GC.enable
|
101
132
|
end
|
133
|
+
|
134
|
+
def with_auto_connect(boolean)
|
135
|
+
existing = ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect
|
136
|
+
ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = boolean
|
137
|
+
yield
|
138
|
+
ensure
|
139
|
+
ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = existing
|
140
|
+
end
|
102
141
|
|
103
142
|
end
|
@@ -66,9 +66,16 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase
|
|
66
66
|
|
67
67
|
should 'alter SQL to limit number of records returned offset by specified amount' do
|
68
68
|
options = { :limit => 3, :offset => 5 }
|
69
|
-
expected_sql =
|
69
|
+
expected_sql = "SELECT * FROM (SELECT TOP 3 * FROM (SELECT TOP 8 * FROM books) AS tmp1) AS tmp2"
|
70
70
|
assert_equal(expected_sql, @connection.add_limit_offset!(@select_sql, options))
|
71
71
|
end
|
72
|
+
|
73
|
+
should 'add locks to deepest sub select in limit offset sql that has a limited tally' do
|
74
|
+
options = { :limit => 3, :offset => 5, :lock => 'WITH (NOLOCK)' }
|
75
|
+
expected_sql = "SELECT * FROM (SELECT TOP 3 * FROM (SELECT TOP 8 * FROM books WITH (NOLOCK)) AS tmp1) AS tmp2"
|
76
|
+
@connection.add_limit_offset! @select_sql, options
|
77
|
+
assert_equal expected_sql, @connection.add_lock!(@select_sql,options)
|
78
|
+
end
|
72
79
|
|
73
80
|
# Not really sure what an offset sql injection might look like
|
74
81
|
should 'not allow sql injection via offset' do
|
@@ -82,6 +89,18 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase
|
|
82
89
|
assert_equal expected_sql, @connection.add_limit_offset!(@subquery_select_sql,options)
|
83
90
|
end
|
84
91
|
|
92
|
+
should 'add lock hints to tally sql if :lock option is present' do
|
93
|
+
assert_sql %r|SELECT TOP 1000000000 \* FROM \[people\] WITH \(NOLOCK\)| do
|
94
|
+
Person.all :limit => 5, :offset => 1, :lock => 'WITH (NOLOCK)'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
should 'not add lock hints to tally sql if there is no :lock option' do
|
99
|
+
assert_sql %r|\(SELECT TOP 1000000000 \* FROM \[people\] \)| do
|
100
|
+
Person.all :limit => 5, :offset => 1
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
85
104
|
end
|
86
105
|
|
87
106
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'cases/sqlserver_helper'
|
2
2
|
|
3
3
|
class StringDefault < ActiveRecord::Base; end;
|
4
|
+
class SqlServerEdgeSchema < ActiveRecord::Base; end;
|
4
5
|
|
5
6
|
class SpecificSchemaTestSqlserver < ActiveRecord::TestCase
|
6
7
|
|
@@ -23,4 +24,34 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase
|
|
23
24
|
assert_equal '(NULL)', default.string_with_pretend_null_four
|
24
25
|
end
|
25
26
|
|
27
|
+
context 'Testing edge case schemas' do
|
28
|
+
|
29
|
+
setup do
|
30
|
+
@edge_class = SqlServerEdgeSchema
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'with description column' do
|
34
|
+
|
35
|
+
setup do
|
36
|
+
@da = @edge_class.create! :description => 'A'
|
37
|
+
@db = @edge_class.create! :description => 'B'
|
38
|
+
@dc = @edge_class.create! :description => 'C'
|
39
|
+
end
|
40
|
+
|
41
|
+
teardown { @edge_class.delete_all }
|
42
|
+
|
43
|
+
should 'allow all sorts of ordering without adapter munging it up' do
|
44
|
+
assert_equal ['A','B','C'], @edge_class.all(:order => 'description').map(&:description)
|
45
|
+
assert_equal ['A','B','C'], @edge_class.all(:order => 'description asc').map(&:description)
|
46
|
+
assert_equal ['A','B','C'], @edge_class.all(:order => 'description ASC').map(&:description)
|
47
|
+
assert_equal ['C','B','A'], @edge_class.all(:order => 'description desc').map(&:description)
|
48
|
+
assert_equal ['C','B','A'], @edge_class.all(:order => 'description DESC').map(&:description)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
|
26
57
|
end
|
@@ -63,6 +63,10 @@ ActiveRecord::Schema.define do
|
|
63
63
|
# TODO: Add some different native binary types and test.
|
64
64
|
end
|
65
65
|
|
66
|
+
create_table :sql_server_edge_schemas, :force => true do |t|
|
67
|
+
t.string :description
|
68
|
+
end
|
69
|
+
|
66
70
|
execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'customers_view') DROP VIEW customers_view"
|
67
71
|
execute <<-CUSTOMERSVIEW
|
68
72
|
CREATE VIEW customers_view AS
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails-sqlserver-2000-2005-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.16
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ken Collins
|
@@ -13,7 +13,7 @@ autorequire:
|
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
15
|
|
16
|
-
date: 2009-
|
16
|
+
date: 2009-04-21 00:00:00 -07:00
|
17
17
|
default_executable:
|
18
18
|
dependencies: []
|
19
19
|
|