aq1018-sqlserver-2000-2008-adpater 0.0.2

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.
@@ -0,0 +1,57 @@
1
+ require 'cases/sqlserver_helper'
2
+ require 'models/person'
3
+
4
+ class MigrationTestSqlserver < ActiveRecord::TestCase
5
+
6
+ context 'For transactions' do
7
+
8
+ setup do
9
+ @connection = ActiveRecord::Base.connection
10
+ @trans_test_table1 = 'sqlserver_trans_table1'
11
+ @trans_test_table2 = 'sqlserver_trans_table2'
12
+ @trans_tables = [@trans_test_table1,@trans_test_table2]
13
+ end
14
+
15
+ teardown do
16
+ @trans_tables.each do |table_name|
17
+ ActiveRecord::Migration.drop_table(table_name) if @connection.tables.include?(table_name)
18
+ end
19
+ end
20
+
21
+ should 'not create a tables if error in migrations' do
22
+ begin
23
+ ActiveRecord::Migrator.up(SQLSERVER_MIGRATIONS_ROOT+'/transaction_table')
24
+ rescue Exception => e
25
+ assert_match %r|this and all later migrations canceled|, e.message
26
+ end
27
+ assert_does_not_contain @trans_test_table1, @connection.tables
28
+ assert_does_not_contain @trans_test_table2, @connection.tables
29
+ end
30
+
31
+ end
32
+
33
+
34
+ end
35
+
36
+
37
+ class MigrationTest < ActiveRecord::TestCase
38
+
39
+ COERCED_TESTS = [:test_add_column_not_null_without_default]
40
+
41
+ include SqlserverCoercedTest
42
+
43
+
44
+ def test_coerced_test_add_column_not_null_without_default
45
+ Person.connection.create_table :testings do |t|
46
+ t.column :foo, :string
47
+ t.column :bar, :string, :null => false
48
+ end
49
+ assert_raises(ActiveRecord::StatementInvalid) do
50
+ Person.connection.execute "INSERT INTO [testings] ([foo], [bar]) VALUES ('hello', NULL)"
51
+ end
52
+ ensure
53
+ Person.connection.drop_table :testings rescue nil
54
+ end
55
+
56
+
57
+ end
@@ -0,0 +1,89 @@
1
+ require 'cases/sqlserver_helper'
2
+ require 'models/book'
3
+
4
+ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase
5
+
6
+ class Account < ActiveRecord::Base; end
7
+
8
+ def setup
9
+ @connection = ActiveRecord::Base.connection
10
+ end
11
+
12
+ context 'When selecting with limit' do
13
+
14
+ setup do
15
+ @select_sql = 'SELECT * FROM schema'
16
+ end
17
+
18
+ should 'alter SQL to limit number of records returned' do
19
+ options = { :limit => 10 }
20
+ assert_equal('SELECT TOP 10 * FROM schema', @connection.add_limit_offset!(@select_sql, options))
21
+ end
22
+
23
+ should 'only allow integers for limit' do
24
+ options = { :limit => 'ten' }
25
+ assert_raise(ArgumentError) {@connection.add_limit_offset!(@select_sql, options) }
26
+ end
27
+
28
+ should 'convert strings which look like integers to integers' do
29
+ options = { :limit => '42' }
30
+ assert_nothing_raised(ArgumentError) {@connection.add_limit_offset!(@select_sql, options)}
31
+ end
32
+
33
+ should 'not allow sql injection via limit' do
34
+ options = { :limit => '1 * FROM schema; DELETE * FROM table; SELECT TOP 10 *'}
35
+ assert_raise(ArgumentError) { @connection.add_limit_offset!(@select_sql, options) }
36
+ end
37
+
38
+ end
39
+
40
+ context 'When selecting with limit and offset' do
41
+
42
+ setup do
43
+ @select_sql = 'SELECT * FROM books'
44
+ @subquery_select_sql = 'SELECT *, (SELECT TOP 1 id FROM books) AS book_id FROM books'
45
+ @books = (1..10).map {|i| Book.create!}
46
+ end
47
+
48
+ teardown do
49
+ @books.each {|b| b.destroy}
50
+ end
51
+
52
+ should 'have limit if offset is passed' do
53
+ options = { :offset => 1 }
54
+ assert_raise(ArgumentError) { @connection.add_limit_offset!(@select_sql, options) }
55
+ end
56
+
57
+ should 'only allow integers for offset' do
58
+ options = { :limit => 10, :offset => 'five' }
59
+ assert_raise(ArgumentError) { @connection.add_limit_offset!(@select_sql, options)}
60
+ end
61
+
62
+ should 'convert strings which look like integers to integers' do
63
+ options = { :limit => 10, :offset => '5' }
64
+ assert_nothing_raised(ArgumentError) {@connection.add_limit_offset!(@select_sql, options)}
65
+ end
66
+
67
+ should 'alter SQL to limit number of records returned offset by specified amount' do
68
+ options = { :limit => 3, :offset => 5 }
69
+ expected_sql = %&SELECT * FROM (SELECT TOP 3 * FROM (SELECT TOP 8 * FROM books) AS tmp1) AS tmp2&
70
+ assert_equal(expected_sql, @connection.add_limit_offset!(@select_sql, options))
71
+ end
72
+
73
+ # Not really sure what an offset sql injection might look like
74
+ should 'not allow sql injection via offset' do
75
+ options = { :limit => 10, :offset => '1 * FROM schema; DELETE * FROM table; SELECT TOP 10 *'}
76
+ assert_raise(ArgumentError) { @connection.add_limit_offset!(@select_sql, options) }
77
+ end
78
+
79
+ should 'not create invalid SQL with subquery SELECTs with TOP' do
80
+ options = { :limit => 5, :offset => 1 }
81
+ expected_sql = "SELECT * FROM (SELECT TOP 5 * FROM (SELECT TOP 6 *, (SELECT TOP 1 id FROM books) AS book_id FROM books) AS tmp1) AS tmp2"
82
+ assert_equal expected_sql, @connection.add_limit_offset!(@subquery_select_sql,options)
83
+ end
84
+
85
+ end
86
+
87
+
88
+ end
89
+
@@ -0,0 +1,100 @@
1
+ require 'cases/sqlserver_helper'
2
+ require 'models/person'
3
+ require 'models/reader'
4
+
5
+ class PessimisticLockingTestSqlserver < ActiveRecord::TestCase
6
+
7
+ self.use_transactional_fixtures = false
8
+ fixtures :people, :readers
9
+
10
+ def setup
11
+ Person.columns; Reader.columns # Avoid introspection queries during tests.
12
+ end
13
+
14
+ context 'For simple finds with default lock option' do
15
+
16
+ should 'lock with simple find' do
17
+ assert_nothing_raised do
18
+ Person.transaction do
19
+ Person.find 1, :lock => true
20
+ end
21
+ end
22
+ end
23
+
24
+ should 'lock with scoped find' do
25
+ assert_nothing_raised do
26
+ Person.transaction do
27
+ Person.with_scope(:find => { :lock => true }) do
28
+ Person.find 1
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ should 'lock with eager find' do
35
+ assert_nothing_raised do
36
+ Person.transaction do
37
+ Person.find 1, :include => :readers, :lock => true
38
+ end
39
+ end
40
+ end
41
+
42
+ should 'reload with lock when #lock! called' do
43
+ assert_nothing_raised do
44
+ Person.transaction do
45
+ person = Person.find 1
46
+ old, person.first_name = person.first_name, 'fooman'
47
+ person.lock!
48
+ assert_equal old, person.first_name
49
+ end
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ context 'For dueling concurrent connections' do
56
+
57
+ use_concurrent_connections
58
+
59
+ should 'no locks does not wait' do
60
+ first, second = duel { Person.find 1 }
61
+ assert first.end > second.end
62
+ end
63
+
64
+ should 'that second lock waits' do
65
+ assert [0.2, 1, 5].any? { |zzz|
66
+ first, second = duel(zzz) { Person.find 1, :lock => true }
67
+ second.end > first.end
68
+ }
69
+ end
70
+
71
+ end
72
+
73
+
74
+ protected
75
+
76
+ def duel(zzz = 5)
77
+ t0, t1, t2, t3 = nil, nil, nil, nil
78
+ a = Thread.new do
79
+ t0 = Time.now
80
+ Person.transaction do
81
+ yield
82
+ sleep zzz # block thread 2 for zzz seconds
83
+ end
84
+ t1 = Time.now
85
+ end
86
+ b = Thread.new do
87
+ sleep zzz / 2.0 # ensure thread 1 tx starts first
88
+ t2 = Time.now
89
+ Person.transaction { yield }
90
+ t3 = Time.now
91
+ end
92
+ a.join
93
+ b.join
94
+ assert t1 > t0 + zzz
95
+ assert t2 > t0
96
+ assert t3 > t2
97
+ [t0.to_f..t1.to_f, t2.to_f..t3.to_f]
98
+ end
99
+
100
+ end
@@ -0,0 +1,24 @@
1
+ require 'cases/sqlserver_helper'
2
+ require 'models/task'
3
+
4
+ class QueryCacheTestSqlserver < ActiveRecord::TestCase
5
+ end
6
+
7
+ class QueryCacheTest < ActiveRecord::TestCase
8
+
9
+ COERCED_TESTS = [:test_cache_does_not_wrap_string_results_in_arrays]
10
+
11
+ include SqlserverCoercedTest
12
+
13
+ fixtures :tasks
14
+
15
+
16
+ def test_coerced_test_cache_does_not_wrap_string_results_in_arrays
17
+ Task.cache do
18
+ assert_instance_of Fixnum, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+
@@ -0,0 +1,61 @@
1
+ require 'cases/sqlserver_helper'
2
+
3
+ class SchemaDumperTestSqlserver < ActiveRecord::TestCase
4
+
5
+ setup :find_all_tables
6
+
7
+ context 'For primary keys' do
8
+
9
+ should 'honor nonstandards' do
10
+ table_dump('movies') do |output|
11
+ match = output.match(%r{create_table "movies"(.*)do})
12
+ assert_not_nil(match, "nonstandardpk table not found")
13
+ assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ context 'For integers' do
20
+
21
+ should 'include limit constraint that match logic for smallint and bigint in #extract_limit' do
22
+ table_dump('integer_limits') do |output|
23
+ assert_match %r{c_int_1.*:limit => 2}, output
24
+ assert_match %r{c_int_2.*:limit => 2}, output
25
+ assert_match %r{c_int_3.*}, output
26
+ assert_match %r{c_int_4.*}, output
27
+ assert_no_match %r{c_int_3.*:limit}, output
28
+ assert_no_match %r{c_int_4.*:limit}, output
29
+ assert_match %r{c_int_5.*:limit => 8}, output
30
+ assert_match %r{c_int_6.*:limit => 8}, output
31
+ assert_match %r{c_int_7.*:limit => 8}, output
32
+ assert_match %r{c_int_8.*:limit => 8}, output
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+
39
+
40
+ private
41
+
42
+ def find_all_tables
43
+ @all_tables ||= ActiveRecord::Base.connection.tables
44
+ end
45
+
46
+ def standard_dump(ignore_tables = [])
47
+ stream = StringIO.new
48
+ ActiveRecord::SchemaDumper.ignore_tables = [*ignore_tables]
49
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
50
+ stream.string
51
+ end
52
+
53
+ def table_dump(*table_names)
54
+ stream = StringIO.new
55
+ ActiveRecord::SchemaDumper.ignore_tables = @all_tables-table_names
56
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
57
+ yield stream.string
58
+ stream.string
59
+ end
60
+
61
+ end
@@ -0,0 +1,25 @@
1
+ require 'cases/sqlserver_helper'
2
+
3
+ class StringDefault < ActiveRecord::Base; end;
4
+
5
+ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase
6
+
7
+ should 'default strings before save' do
8
+ default = StringDefault.new
9
+ assert_equal nil, default.string_with_null_default
10
+ assert_equal 'null', default.string_with_pretend_null_one
11
+ assert_equal '(null)', default.string_with_pretend_null_two
12
+ assert_equal 'NULL', default.string_with_pretend_null_three
13
+ assert_equal '(NULL)', default.string_with_pretend_null_four
14
+ end
15
+
16
+ should 'default strings after save' do
17
+ default = StringDefault.create
18
+ assert_equal nil, default.string_with_null_default
19
+ assert_equal 'null', default.string_with_pretend_null_one
20
+ assert_equal '(null)', default.string_with_pretend_null_two
21
+ assert_equal 'NULL', default.string_with_pretend_null_three
22
+ assert_equal '(NULL)', default.string_with_pretend_null_four
23
+ end
24
+
25
+ end
@@ -0,0 +1,100 @@
1
+ require 'rubygems'
2
+ require 'shoulda'
3
+ require 'mocha'
4
+ require 'cases/helper'
5
+ require 'models/topic'
6
+
7
+ SQLSERVER_TEST_ROOT = File.expand_path(File.join(File.dirname(__FILE__),'..'))
8
+ SQLSERVER_ASSETS_ROOT = SQLSERVER_TEST_ROOT + "/assets"
9
+ SQLSERVER_FIXTURES_ROOT = SQLSERVER_TEST_ROOT + "/fixtures"
10
+ SQLSERVER_MIGRATIONS_ROOT = SQLSERVER_TEST_ROOT + "/migrations"
11
+ SQLSERVER_SCHEMA_ROOT = SQLSERVER_TEST_ROOT + "/schema"
12
+ ACTIVERECORD_TEST_ROOT = File.expand_path(SQLSERVER_TEST_ROOT + "/../../../../vendor/rails/activerecord/test/")
13
+
14
+ ActiveRecord::Migration.verbose = false
15
+
16
+ # Defining our classes in one place as well as soem core tests that need coercing date/time types.
17
+
18
+ class TableWithRealColumn < ActiveRecord::Base; end
19
+ class FkTestHasFk < ActiveRecord::Base ; end
20
+ class FkTestHasPk < ActiveRecord::Base ; end
21
+ class NumericData < ActiveRecord::Base ; self.table_name = 'numeric_data' ; end
22
+ class CustomersView < ActiveRecord::Base ; self.table_name = 'customers_view' ; end
23
+ class StringDefaultsView < ActiveRecord::Base ; self.table_name = 'string_defaults_view' ; end
24
+ class SqlServerUnicode < ActiveRecord::Base ; end
25
+ class SqlServerString < ActiveRecord::Base ; end
26
+
27
+ class SqlServerChronic < ActiveRecord::Base
28
+ coerce_sqlserver_date :date
29
+ coerce_sqlserver_time :time
30
+ default_timezone = :utc
31
+ end
32
+
33
+ class Topic < ActiveRecord::Base
34
+ coerce_sqlserver_date :last_read
35
+ coerce_sqlserver_time :bonus_time
36
+ end
37
+ class Person < ActiveRecord::Base
38
+ coerce_sqlserver_date :favorite_day
39
+ end
40
+
41
+ # A module that we can include in classes where we want to override an active record test.
42
+
43
+ module SqlserverCoercedTest
44
+ def self.included(base)
45
+ base.extend ClassMethods
46
+ end
47
+ module ClassMethods
48
+ def coerced_tests
49
+ self.const_get(:COERCED_TESTS) rescue nil
50
+ end
51
+ def method_added(method)
52
+ undef_method(method) if coerced_tests && coerced_tests.include?(method)
53
+ end
54
+ end
55
+ end
56
+
57
+ # Change the text database type to support ActiveRecord's tests for = on text columns which
58
+ # is not supported in SQL Server text columns, so use varchar(8000) instead.
59
+
60
+ if ActiveRecord::Base.connection.sqlserver_2000?
61
+ ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = 'varchar(8000)'
62
+ end
63
+
64
+ # Our changes/additions to ActiveRecord test helpers specific for SQL Server.
65
+
66
+ ActiveRecord::Base.connection.class.class_eval do
67
+ IGNORED_SQL << /SELECT SCOPE_IDENTITY/ << /INFORMATION_SCHEMA.TABLES/ << /INFORMATION_SCHEMA.COLUMNS/
68
+ end
69
+
70
+ ActiveRecord::ConnectionAdapters::SQLServerAdapter.class_eval do
71
+ def raw_select_with_query_record(sql, name = nil)
72
+ $queries_executed ||= []
73
+ $queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
74
+ raw_select_without_query_record(sql,name)
75
+ end
76
+ alias_method_chain :raw_select, :query_record
77
+ end
78
+
79
+ module ActiveRecord
80
+ class TestCase < ActiveSupport::TestCase
81
+ class << self
82
+ def sqlserver_2000? ; ActiveRecord::Base.connection.sqlserver_2000? ; end
83
+ def sqlserver_2005? ; ActiveRecord::Base.connection.sqlserver_2005? ; end
84
+ end
85
+ def assert_sql(*patterns_to_match)
86
+ $queries_executed = []
87
+ yield
88
+ ensure
89
+ failed_patterns = []
90
+ patterns_to_match.each do |pattern|
91
+ failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
92
+ end
93
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found in:\n#{$queries_executed.inspect}"
94
+ end
95
+ def sqlserver_2000? ; self.class.sqlserver_2000? ; end
96
+ def sqlserver_2005? ; self.class.sqlserver_2005? ; end
97
+ end
98
+ end
99
+
100
+