activerecord-sqlserver-adapter 2.2.18

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/CHANGELOG +175 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Manifest +36 -0
  4. data/README.rdoc +175 -0
  5. data/RUNNING_UNIT_TESTS +60 -0
  6. data/Rakefile +18 -0
  7. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +1126 -0
  8. data/lib/activerecord-sqlserver-adapter.rb +1 -0
  9. data/lib/core_ext/active_record.rb +133 -0
  10. data/lib/core_ext/dbi.rb +85 -0
  11. data/tasks/sqlserver.rake +31 -0
  12. data/test/cases/aaaa_create_tables_test_sqlserver.rb +19 -0
  13. data/test/cases/adapter_test_sqlserver.rb +707 -0
  14. data/test/cases/attribute_methods_test_sqlserver.rb +33 -0
  15. data/test/cases/basics_test_sqlserver.rb +21 -0
  16. data/test/cases/calculations_test_sqlserver.rb +20 -0
  17. data/test/cases/column_test_sqlserver.rb +264 -0
  18. data/test/cases/connection_test_sqlserver.rb +142 -0
  19. data/test/cases/eager_association_test_sqlserver.rb +42 -0
  20. data/test/cases/execute_procedure_test_sqlserver.rb +33 -0
  21. data/test/cases/inheritance_test_sqlserver.rb +28 -0
  22. data/test/cases/method_scoping_test_sqlserver.rb +28 -0
  23. data/test/cases/migration_test_sqlserver.rb +93 -0
  24. data/test/cases/offset_and_limit_test_sqlserver.rb +108 -0
  25. data/test/cases/pessimistic_locking_test_sqlserver.rb +125 -0
  26. data/test/cases/query_cache_test_sqlserver.rb +24 -0
  27. data/test/cases/schema_dumper_test_sqlserver.rb +72 -0
  28. data/test/cases/specific_schema_test_sqlserver.rb +57 -0
  29. data/test/cases/sqlserver_helper.rb +123 -0
  30. data/test/cases/table_name_test_sqlserver.rb +22 -0
  31. data/test/cases/transaction_test_sqlserver.rb +93 -0
  32. data/test/cases/unicode_test_sqlserver.rb +50 -0
  33. data/test/connections/native_sqlserver/connection.rb +23 -0
  34. data/test/connections/native_sqlserver_odbc/connection.rb +25 -0
  35. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
  36. data/test/schema/sqlserver_specific_schema.rb +91 -0
  37. metadata +120 -0
@@ -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,72 @@
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
+ context 'For strings' do
39
+
40
+ should 'have varchar(max) dumped as text' do
41
+ table_dump('sql_server_strings') do |output|
42
+ assert_match %r{t.text.*varchar_max}, output
43
+ end
44
+ end if sqlserver_2005? || sqlserver_2008?
45
+
46
+ end
47
+
48
+
49
+
50
+
51
+ private
52
+
53
+ def find_all_tables
54
+ @all_tables ||= ActiveRecord::Base.connection.tables
55
+ end
56
+
57
+ def standard_dump(ignore_tables = [])
58
+ stream = StringIO.new
59
+ ActiveRecord::SchemaDumper.ignore_tables = [*ignore_tables]
60
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
61
+ stream.string
62
+ end
63
+
64
+ def table_dump(*table_names)
65
+ stream = StringIO.new
66
+ ActiveRecord::SchemaDumper.ignore_tables = @all_tables-table_names
67
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
68
+ yield stream.string
69
+ stream.string
70
+ end
71
+
72
+ end
@@ -0,0 +1,57 @@
1
+ require 'cases/sqlserver_helper'
2
+
3
+ class StringDefault < ActiveRecord::Base; end;
4
+ class SqlServerEdgeSchema < ActiveRecord::Base; end;
5
+
6
+ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase
7
+
8
+ should 'default strings before save' do
9
+ default = StringDefault.new
10
+ assert_equal nil, default.string_with_null_default
11
+ assert_equal 'null', default.string_with_pretend_null_one
12
+ assert_equal '(null)', default.string_with_pretend_null_two
13
+ assert_equal 'NULL', default.string_with_pretend_null_three
14
+ assert_equal '(NULL)', default.string_with_pretend_null_four
15
+ assert_equal '(3)', default.string_with_pretend_paren_three
16
+ end
17
+
18
+ should 'default strings after save' do
19
+ default = StringDefault.create
20
+ assert_equal nil, default.string_with_null_default
21
+ assert_equal 'null', default.string_with_pretend_null_one
22
+ assert_equal '(null)', default.string_with_pretend_null_two
23
+ assert_equal 'NULL', default.string_with_pretend_null_three
24
+ assert_equal '(NULL)', default.string_with_pretend_null_four
25
+ end
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
+
57
+ end
@@ -0,0 +1,123 @@
1
+ require 'rubygems'
2
+ require 'shoulda'
3
+ require 'mocha'
4
+ require 'cases/helper'
5
+ require 'models/topic'
6
+ require 'active_record/version'
7
+
8
+ SQLSERVER_TEST_ROOT = File.expand_path(File.join(File.dirname(__FILE__),'..'))
9
+ SQLSERVER_ASSETS_ROOT = SQLSERVER_TEST_ROOT + "/assets"
10
+ SQLSERVER_FIXTURES_ROOT = SQLSERVER_TEST_ROOT + "/fixtures"
11
+ SQLSERVER_MIGRATIONS_ROOT = SQLSERVER_TEST_ROOT + "/migrations"
12
+ SQLSERVER_SCHEMA_ROOT = SQLSERVER_TEST_ROOT + "/schema"
13
+ ACTIVERECORD_TEST_ROOT = File.expand_path(SQLSERVER_TEST_ROOT + "/../../../../rails/activerecord/test/")
14
+
15
+ ActiveRecord::Migration.verbose = false
16
+
17
+ # Defining our classes in one place as well as soem core tests that need coercing date/time types.
18
+
19
+ class TableWithRealColumn < ActiveRecord::Base; end
20
+ class FkTestHasFk < ActiveRecord::Base ; end
21
+ class FkTestHasPk < ActiveRecord::Base ; end
22
+ class NumericData < ActiveRecord::Base ; self.table_name = 'numeric_data' ; end
23
+ class CustomersView < ActiveRecord::Base ; self.table_name = 'customers_view' ; end
24
+ class StringDefaultsView < ActiveRecord::Base ; self.table_name = 'string_defaults_view' ; end
25
+ class StringDefaultsBigView < ActiveRecord::Base ; self.table_name = 'string_defaults_big_view' ; end
26
+ class SqlServerUnicode < ActiveRecord::Base ; end
27
+ class SqlServerString < ActiveRecord::Base ; end
28
+ class SqlServerChronic < ActiveRecord::Base
29
+ coerce_sqlserver_date :date
30
+ coerce_sqlserver_time :time
31
+ default_timezone = :utc
32
+ end
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
+ if coerced_tests && coerced_tests.include?(method)
53
+ undef_method(method)
54
+ STDOUT.puts("Undefined coerced test: #{self.name}##{method}")
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ # Set weather to test unicode string defaults or not. Used from rake task.
61
+
62
+ if ENV['ENABLE_DEFAULT_UNICODE_TYPES'] == 'true'
63
+ puts "With enabled unicode string types"
64
+ ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types = true
65
+ end
66
+
67
+ # Change the text database type to support ActiveRecord's tests for = on text columns which
68
+ # is not supported in SQL Server text columns, so use varchar(8000) instead.
69
+
70
+ if ActiveRecord::Base.connection.sqlserver_2000?
71
+ if ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types
72
+ ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = 'nvarchar(4000)'
73
+ else
74
+ ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = 'varchar(8000)'
75
+ end
76
+ end
77
+
78
+ # Our changes/additions to ActiveRecord test helpers specific for SQL Server.
79
+
80
+ ActiveRecord::Base.connection.class.class_eval do
81
+ IGNORED_SQL << %r|SELECT SCOPE_IDENTITY| << %r{INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)}
82
+ IGNORED_SQL << %r|SELECT @@IDENTITY| << %r|SELECT @@ROWCOUNT| << %r|SELECT @@version| << %r|SELECT @@TRANCOUNT|
83
+ end
84
+
85
+ ActiveRecord::ConnectionAdapters::SQLServerAdapter.class_eval do
86
+ def raw_select_with_query_record(sql, name = nil)
87
+ $queries_executed ||= []
88
+ $queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
89
+ raw_select_without_query_record(sql,name)
90
+ end
91
+ alias_method_chain :raw_select, :query_record
92
+ end
93
+
94
+ module ActiveRecord
95
+ class TestCase < ActiveSupport::TestCase
96
+ class << self
97
+ def sqlserver_2000? ; ActiveRecord::Base.connection.sqlserver_2000? ; end
98
+ def sqlserver_2005? ; ActiveRecord::Base.connection.sqlserver_2005? ; end
99
+ def sqlserver_2008? ; ActiveRecord::Base.connection.sqlserver_2008? ; end
100
+ def active_record_2_point_2? ; ActiveRecord::VERSION::MAJOR == 2 && ActiveRecord::VERSION::MINOR == 2 ; end
101
+ def active_record_2_point_3? ; ActiveRecord::VERSION::MAJOR == 2 && ActiveRecord::VERSION::MINOR == 3 ; end
102
+ def ruby_19? ; RUBY_VERSION >= '1.9' ; end
103
+ end
104
+ def assert_sql(*patterns_to_match)
105
+ $queries_executed = []
106
+ yield
107
+ ensure
108
+ failed_patterns = []
109
+ patterns_to_match.each do |pattern|
110
+ failed_patterns << pattern unless $queries_executed.any?{ |sql| pattern === sql }
111
+ end
112
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found in:\n#{$queries_executed.inspect}"
113
+ end
114
+ def sqlserver_2000? ; self.class.sqlserver_2000? ; end
115
+ def sqlserver_2005? ; self.class.sqlserver_2005? ; end
116
+ def sqlserver_2008? ; self.class.sqlserver_2008? ; end
117
+ def active_record_2_point_2? ; self.class.active_record_2_point_2? ; end
118
+ def active_record_2_point_3? ; self.class.active_record_2_point_3? ; end
119
+ def ruby_19? ; self.class.ruby_19? ; end
120
+ end
121
+ end
122
+
123
+
@@ -0,0 +1,22 @@
1
+ require 'cases/sqlserver_helper'
2
+ require 'models/order'
3
+
4
+ class TableNameTestSqlserver < ActiveRecord::TestCase
5
+
6
+ self.use_transactional_fixtures = false
7
+
8
+ def setup
9
+ Order.table_name = '[orders]'
10
+ Order.reset_column_information
11
+ end
12
+
13
+ should 'load columns with escaped table name for model' do
14
+ assert_equal 4, Order.columns.length
15
+ end
16
+
17
+ should 'not re-escape table name if it is escaped already for SQL queries' do
18
+ assert_sql(/SELECT \* FROM \[orders\]/) { Order.all }
19
+ end
20
+
21
+
22
+ end
@@ -0,0 +1,93 @@
1
+ require 'cases/sqlserver_helper'
2
+ require 'models/ship'
3
+ require 'models/developer'
4
+
5
+ class TransactionTestSqlserver < ActiveRecord::TestCase
6
+
7
+ self.use_transactional_fixtures = false
8
+
9
+ setup :delete_ships
10
+
11
+ context 'Testing transaction basics' do
12
+
13
+ should 'allow ActiveRecord::Rollback to work in 1 transaction block' do
14
+ Ship.transaction do
15
+ Ship.create! :name => 'Black Pearl'
16
+ raise ActiveRecord::Rollback
17
+ end
18
+ assert_no_ships
19
+ end
20
+
21
+ should 'allow nested transactions to totally rollback' do
22
+ begin
23
+ Ship.transaction do
24
+ Ship.create! :name => 'Black Pearl'
25
+ Ship.transaction do
26
+ Ship.create! :name => 'Flying Dutchman'
27
+ raise 'HELL'
28
+ end
29
+ end
30
+ rescue Exception => e
31
+ assert_no_ships
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ context 'Testing #outside_transaction?' do
38
+
39
+ should 'work in simple usage' do
40
+ assert Ship.connection.outside_transaction?
41
+ Ship.connection.begin_db_transaction
42
+ assert !Ship.connection.outside_transaction?
43
+ Ship.connection.rollback_db_transaction
44
+ assert Ship.connection.outside_transaction?
45
+ end
46
+
47
+ should 'work inside nested transactions' do
48
+ assert Ship.connection.outside_transaction?
49
+ Ship.transaction do
50
+ assert !Ship.connection.outside_transaction?
51
+ Ship.transaction do
52
+ assert !Ship.connection.outside_transaction?
53
+ end
54
+ end
55
+ assert Ship.connection.outside_transaction?
56
+ end
57
+
58
+ should 'not call rollback if no transaction is active' do
59
+ assert_raise RuntimeError do
60
+ Ship.transaction do
61
+ Ship.connection.rollback_db_transaction
62
+ Ship.connection.expects(:rollback_db_transaction).never
63
+ raise "Rails doesn't scale!"
64
+ end
65
+ end
66
+ end
67
+
68
+ should 'test_open_transactions_count_is_reset_to_zero_if_no_transaction_active' do
69
+ Ship.transaction do
70
+ Ship.transaction do
71
+ Ship.connection.rollback_db_transaction
72
+ end
73
+ assert_equal 0, Ship.connection.open_transactions
74
+ end
75
+ assert_equal 0, Ship.connection.open_transactions
76
+ end
77
+
78
+ end unless active_record_2_point_2?
79
+
80
+
81
+
82
+ protected
83
+
84
+ def delete_ships
85
+ Ship.delete_all
86
+ end
87
+
88
+ def assert_no_ships
89
+ assert Ship.count.zero?, "Expected Ship to have no models but it did have:\n#{Ship.all.inspect}"
90
+ end
91
+
92
+ end
93
+
@@ -0,0 +1,50 @@
1
+ require 'cases/sqlserver_helper'
2
+
3
+ class UnicodeTestSqlserver < ActiveRecord::TestCase
4
+
5
+
6
+ context 'Testing basic saves and unicode limits' do
7
+
8
+ should 'save and reload simple nchar string' do
9
+ assert nchar_data = SqlServerUnicode.create!(:nchar => 'A')
10
+ assert_equal 'A', SqlServerUnicode.find(nchar_data.id).nchar
11
+ end
12
+
13
+ should 'save and reload simple nvarchar(max) string' do
14
+ test_string = 'Ken Collins'
15
+ assert nvarcharmax_data = SqlServerUnicode.create!(:nvarchar_max => test_string)
16
+ assert_equal test_string, SqlServerUnicode.find(nvarcharmax_data.id).nvarchar_max
17
+ end if sqlserver_2005? || sqlserver_2008?
18
+
19
+ should 'enforce default nchar_10 limit of 10' do
20
+ assert_raise(ActiveRecord::StatementInvalid) { SqlServerUnicode.create!(:nchar => '01234567891') }
21
+ end
22
+
23
+ should 'enforce default nvarchar_100 limit of 100' do
24
+ assert_raise(ActiveRecord::StatementInvalid) { SqlServerUnicode.create!(:nvarchar_100 => '0123456789'*10+'1') }
25
+ end
26
+
27
+ end
28
+
29
+ context 'Testing unicode data' do
30
+
31
+ setup do
32
+ @unicode_data = "\344\270\200\344\272\21434\344\272\224\345\205\255"
33
+ @encoded_unicode_data = "\344\270\200\344\272\21434\344\272\224\345\205\255".force_encoding('UTF-8') if ruby_19?
34
+ end
35
+
36
+ should 'insert into nvarchar field' do
37
+ assert data = SqlServerUnicode.create!(:nvarchar => @unicode_data)
38
+ assert_equal @unicode_data, data.reload.nvarchar
39
+ end
40
+
41
+ should 're-encode data on DB reads' do
42
+ assert data = SqlServerUnicode.create!(:nvarchar => @unicode_data)
43
+ assert_equal @encoded_unicode_data, data.reload.nvarchar
44
+ end if ruby_19?
45
+
46
+ end
47
+
48
+
49
+
50
+ end