activerecord-sqlserver-adapter 2.2.18

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.
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