activerecord 1.14.4 → 1.15.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (159) hide show
  1. data/CHANGELOG +400 -1
  2. data/README +2 -2
  3. data/RUNNING_UNIT_TESTS +21 -3
  4. data/Rakefile +55 -10
  5. data/lib/active_record.rb +10 -4
  6. data/lib/active_record/acts/list.rb +15 -4
  7. data/lib/active_record/acts/nested_set.rb +11 -12
  8. data/lib/active_record/acts/tree.rb +13 -14
  9. data/lib/active_record/aggregations.rb +46 -22
  10. data/lib/active_record/associations.rb +213 -162
  11. data/lib/active_record/associations/association_collection.rb +45 -15
  12. data/lib/active_record/associations/association_proxy.rb +32 -13
  13. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
  14. data/lib/active_record/associations/has_many_association.rb +37 -17
  15. data/lib/active_record/associations/has_many_through_association.rb +120 -30
  16. data/lib/active_record/associations/has_one_association.rb +1 -1
  17. data/lib/active_record/attribute_methods.rb +75 -0
  18. data/lib/active_record/base.rb +282 -203
  19. data/lib/active_record/calculations.rb +95 -54
  20. data/lib/active_record/callbacks.rb +13 -24
  21. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
  24. data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
  25. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
  26. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
  27. data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
  28. data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
  29. data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
  30. data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
  31. data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
  32. data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
  33. data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
  34. data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
  35. data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
  36. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
  37. data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
  38. data/lib/active_record/deprecated_associations.rb +24 -10
  39. data/lib/active_record/deprecated_finders.rb +4 -1
  40. data/lib/active_record/fixtures.rb +37 -23
  41. data/lib/active_record/locking/optimistic.rb +106 -0
  42. data/lib/active_record/locking/pessimistic.rb +77 -0
  43. data/lib/active_record/migration.rb +8 -5
  44. data/lib/active_record/observer.rb +73 -34
  45. data/lib/active_record/reflection.rb +21 -7
  46. data/lib/active_record/schema_dumper.rb +33 -5
  47. data/lib/active_record/timestamp.rb +23 -34
  48. data/lib/active_record/transactions.rb +37 -30
  49. data/lib/active_record/validations.rb +46 -30
  50. data/lib/active_record/vendor/mysql.rb +20 -5
  51. data/lib/active_record/version.rb +2 -2
  52. data/lib/active_record/wrappings.rb +1 -2
  53. data/lib/active_record/xml_serialization.rb +308 -0
  54. data/test/aaa_create_tables_test.rb +5 -1
  55. data/test/abstract_unit.rb +18 -8
  56. data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
  57. data/test/adapter_test.rb +9 -7
  58. data/test/adapter_test_sqlserver.rb +81 -0
  59. data/test/aggregations_test.rb +29 -0
  60. data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
  61. data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
  62. data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
  63. data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
  64. data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
  65. data/test/associations_test.rb +339 -45
  66. data/test/attribute_methods_test.rb +49 -0
  67. data/test/base_test.rb +321 -67
  68. data/test/calculations_test.rb +48 -10
  69. data/test/callbacks_test.rb +13 -0
  70. data/test/connection_test_firebird.rb +8 -0
  71. data/test/connections/native_db2/connection.rb +18 -17
  72. data/test/connections/native_firebird/connection.rb +19 -17
  73. data/test/connections/native_frontbase/connection.rb +27 -0
  74. data/test/connections/native_mysql/connection.rb +18 -15
  75. data/test/connections/native_openbase/connection.rb +14 -15
  76. data/test/connections/native_oracle/connection.rb +16 -12
  77. data/test/connections/native_postgresql/connection.rb +16 -17
  78. data/test/connections/native_sqlite/connection.rb +3 -6
  79. data/test/connections/native_sqlite3/connection.rb +3 -6
  80. data/test/connections/native_sqlserver/connection.rb +16 -17
  81. data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
  82. data/test/connections/native_sybase/connection.rb +16 -17
  83. data/test/datatype_test_postgresql.rb +52 -0
  84. data/test/defaults_test.rb +52 -10
  85. data/test/deprecated_associations_test.rb +151 -107
  86. data/test/deprecated_finder_test.rb +83 -66
  87. data/test/empty_date_time_test.rb +25 -0
  88. data/test/finder_test.rb +118 -11
  89. data/test/fixtures/accounts.yml +6 -1
  90. data/test/fixtures/author.rb +27 -4
  91. data/test/fixtures/categorizations.yml +8 -2
  92. data/test/fixtures/category.rb +1 -2
  93. data/test/fixtures/comments.yml +0 -6
  94. data/test/fixtures/companies.yml +6 -1
  95. data/test/fixtures/company.rb +23 -1
  96. data/test/fixtures/company_in_module.rb +8 -10
  97. data/test/fixtures/customer.rb +2 -2
  98. data/test/fixtures/customers.yml +9 -0
  99. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  100. data/test/fixtures/db_definitions/db2.sql +9 -0
  101. data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
  102. data/test/fixtures/db_definitions/firebird.sql +13 -1
  103. data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
  104. data/test/fixtures/db_definitions/frontbase.sql +262 -0
  105. data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
  106. data/test/fixtures/db_definitions/frontbase2.sql +4 -0
  107. data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
  108. data/test/fixtures/db_definitions/mysql.sql +23 -14
  109. data/test/fixtures/db_definitions/openbase.sql +13 -1
  110. data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
  111. data/test/fixtures/db_definitions/oracle.sql +29 -2
  112. data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
  113. data/test/fixtures/db_definitions/postgresql.sql +13 -3
  114. data/test/fixtures/db_definitions/schema.rb +29 -1
  115. data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
  116. data/test/fixtures/db_definitions/sqlite.sql +12 -3
  117. data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
  118. data/test/fixtures/db_definitions/sqlserver.sql +35 -0
  119. data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
  120. data/test/fixtures/db_definitions/sybase.sql +13 -4
  121. data/test/fixtures/developer.rb +12 -0
  122. data/test/fixtures/edge.rb +5 -0
  123. data/test/fixtures/edges.yml +6 -0
  124. data/test/fixtures/funny_jokes.yml +3 -7
  125. data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
  126. data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
  127. data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
  128. data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
  129. data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
  130. data/test/fixtures/mixin.rb +15 -0
  131. data/test/fixtures/mixins.yml +38 -0
  132. data/test/fixtures/post.rb +3 -2
  133. data/test/fixtures/project.rb +3 -1
  134. data/test/fixtures/topic.rb +6 -1
  135. data/test/fixtures/topics.yml +4 -4
  136. data/test/fixtures/vertex.rb +9 -0
  137. data/test/fixtures/vertices.yml +4 -0
  138. data/test/fixtures_test.rb +45 -0
  139. data/test/inheritance_test.rb +67 -6
  140. data/test/lifecycle_test.rb +40 -19
  141. data/test/locking_test.rb +170 -26
  142. data/test/method_scoping_test.rb +2 -2
  143. data/test/migration_test.rb +387 -110
  144. data/test/migration_test_firebird.rb +124 -0
  145. data/test/mixin_nested_set_test.rb +14 -2
  146. data/test/mixin_test.rb +56 -18
  147. data/test/modules_test.rb +8 -2
  148. data/test/multiple_db_test.rb +2 -2
  149. data/test/pk_test.rb +1 -0
  150. data/test/reflection_test.rb +8 -2
  151. data/test/schema_authorization_test_postgresql.rb +75 -0
  152. data/test/schema_dumper_test.rb +40 -4
  153. data/test/table_name_test_sqlserver.rb +23 -0
  154. data/test/threaded_connections_test.rb +19 -16
  155. data/test/transactions_test.rb +86 -72
  156. data/test/validations_test.rb +126 -56
  157. data/test/xml_serialization_test.rb +125 -0
  158. metadata +45 -11
  159. data/lib/active_record/locking.rb +0 -79
@@ -46,13 +46,22 @@ end
46
46
 
47
47
  class MultiObserver < ActiveRecord::Observer
48
48
  attr_reader :record
49
-
49
+
50
50
  def self.observed_class() [ Topic, Developer ] end
51
51
 
52
+ cattr_reader :last_inherited
53
+ @@last_inherited = nil
54
+
55
+ def observed_class_inherited_with_testing(subclass)
56
+ observed_class_inherited_without_testing(subclass)
57
+ @@last_inherited = subclass
58
+ end
59
+
60
+ alias_method_chain :observed_class_inherited, :testing
61
+
52
62
  def after_find(record)
53
63
  @record = record
54
64
  end
55
-
56
65
  end
57
66
 
58
67
  class LifecycleTest < Test::Unit::TestCase
@@ -63,54 +72,66 @@ class LifecycleTest < Test::Unit::TestCase
63
72
  Topic.find(1).destroy
64
73
  assert_equal 0, Topic.count
65
74
  end
66
-
75
+
67
76
  def test_after_save
68
77
  ActiveRecord::Base.observers = :topic_manual_observer
78
+ ActiveRecord::Base.instantiate_observers
69
79
 
70
80
  topic = Topic.find(1)
71
81
  topic.title = "hello"
72
82
  topic.save
73
-
83
+
74
84
  assert TopicManualObserver.instance.has_been_notified?
75
85
  assert_equal :after_save, TopicManualObserver.instance.callbacks.last["callback_method"]
76
86
  end
77
-
87
+
78
88
  def test_observer_update_on_save
79
89
  ActiveRecord::Base.observers = TopicManualObserver
90
+ ActiveRecord::Base.instantiate_observers
80
91
 
81
- topic = Topic.find(1)
92
+ topic = Topic.find(1)
82
93
  assert TopicManualObserver.instance.has_been_notified?
83
94
  assert_equal :after_find, TopicManualObserver.instance.callbacks.first["callback_method"]
84
95
  end
85
-
96
+
86
97
  def test_auto_observer
87
98
  topic_observer = TopicaObserver.instance
88
99
 
89
- topic = Topic.find(1)
90
- assert_equal topic_observer.topic.title, topic.title
100
+ topic = Topic.find(1)
101
+ assert_equal topic.title, topic_observer.topic.title
91
102
  end
92
-
93
- def test_infered_auto_observer
103
+
104
+ def test_inferred_auto_observer
94
105
  topic_observer = TopicObserver.instance
95
106
 
96
- topic = Topic.find(1)
97
- assert_equal topic_observer.topic.title, topic.title
107
+ topic = Topic.find(1)
108
+ assert_equal topic.title, topic_observer.topic.title
98
109
  end
99
-
110
+
100
111
  def test_observing_two_classes
101
112
  multi_observer = MultiObserver.instance
102
113
 
103
114
  topic = Topic.find(1)
104
- assert_equal multi_observer.record.title, topic.title
115
+ assert_equal topic.title, multi_observer.record.title
105
116
 
106
- developer = Developer.find(1)
107
- assert_equal multi_observer.record.name, developer.name
117
+ developer = Developer.find(1)
118
+ assert_equal developer.name, multi_observer.record.name
108
119
  end
109
-
120
+
110
121
  def test_observing_subclasses
111
122
  multi_observer = MultiObserver.instance
112
123
 
113
124
  developer = SpecialDeveloper.find(1)
114
- assert_equal multi_observer.record.name, developer.name
125
+ assert_equal developer.name, multi_observer.record.name
126
+
127
+ klass = Class.new(Developer)
128
+ assert_equal klass, multi_observer.last_inherited
129
+
130
+ developer = klass.find(1)
131
+ assert_equal developer.name, multi_observer.record.name
132
+ end
133
+
134
+ def test_invalid_observer
135
+ assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
115
136
  end
116
137
  end
@@ -2,45 +2,189 @@ require 'abstract_unit'
2
2
  require 'fixtures/person'
3
3
  require 'fixtures/legacy_thing'
4
4
 
5
- class LockingTest < Test::Unit::TestCase
5
+ class LockWithoutDefault < ActiveRecord::Base; end
6
+
7
+ class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
8
+ set_table_name :lock_without_defaults_cust
9
+ set_locking_column :custom_lock_version
10
+ end
11
+
12
+ class OptimisticLockingTest < Test::Unit::TestCase
6
13
  fixtures :people, :legacy_things
7
14
 
8
15
  def test_lock_existing
9
16
  p1 = Person.find(1)
10
17
  p2 = Person.find(1)
11
-
12
- p1.first_name = "Michael"
13
- p1.save
14
-
15
- assert_raises(ActiveRecord::StaleObjectError) {
16
- p2.first_name = "should fail"
17
- p2.save
18
- }
18
+ assert_equal 0, p1.lock_version
19
+ assert_equal 0, p2.lock_version
20
+
21
+ p1.save!
22
+ assert_equal 1, p1.lock_version
23
+ assert_equal 0, p2.lock_version
24
+
25
+ assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
19
26
  end
20
27
 
21
28
  def test_lock_new
22
- p1 = Person.create({ "first_name"=>"anika"})
29
+ p1 = Person.new(:first_name => 'anika')
30
+ assert_equal 0, p1.lock_version
31
+
32
+ p1.save!
23
33
  p2 = Person.find(p1.id)
24
- assert_equal p1.id, p2.id
25
- p1.first_name = "Anika"
26
- p1.save
27
-
28
- assert_raises(ActiveRecord::StaleObjectError) {
29
- p2.first_name = "should fail"
30
- p2.save
31
- }
34
+ assert_equal 0, p1.lock_version
35
+ assert_equal 0, p2.lock_version
36
+
37
+ p1.save!
38
+ assert_equal 1, p1.lock_version
39
+ assert_equal 0, p2.lock_version
40
+
41
+ assert_raises(ActiveRecord::StaleObjectError) { p2.save! }
32
42
  end
33
-
43
+
34
44
  def test_lock_column_name_existing
35
45
  t1 = LegacyThing.find(1)
36
46
  t2 = LegacyThing.find(1)
37
- t1.tps_report_number = 400
38
- t1.save
47
+ assert_equal 0, t1.version
48
+ assert_equal 0, t2.version
49
+
50
+ t1.save!
51
+ assert_equal 1, t1.version
52
+ assert_equal 0, t2.version
39
53
 
40
- assert_raises(ActiveRecord::StaleObjectError) {
41
- t2.tps_report_number = 300
42
- t2.save
43
- }
44
- end
54
+ assert_raises(ActiveRecord::StaleObjectError) { t2.save! }
55
+ end
56
+
57
+ def test_lock_column_is_mass_assignable
58
+ p1 = Person.create(:first_name => 'bianca')
59
+ assert_equal 0, p1.lock_version
60
+ assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
61
+
62
+ p1.save!
63
+ assert_equal 1, p1.lock_version
64
+ assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
65
+ end
66
+
67
+ def test_lock_without_default_sets_version_to_zero
68
+ t1 = LockWithoutDefault.new
69
+ assert_equal 0, t1.lock_version
70
+ end
71
+
72
+ def test_lock_with_custom_column_without_default_sets_version_to_zero
73
+ t1 = LockWithCustomColumnWithoutDefault.new
74
+ assert_equal 0, t1.custom_lock_version
75
+ end
76
+ end
45
77
 
78
+
79
+ # TODO: test against the generated SQL since testing locking behavior itself
80
+ # is so cumbersome. Will deadlock Ruby threads if the underlying db.execute
81
+ # blocks, so separate script called by Kernel#system is needed.
82
+ # (See exec vs. async_exec in the PostgreSQL adapter.)
83
+
84
+ # TODO: The SQL Server and Sybase adapters currently have no support for pessimistic locking
85
+
86
+ unless current_adapter?(:SQLServerAdapter, :SybaseAdapter)
87
+ class PessimisticLockingTest < Test::Unit::TestCase
88
+ self.use_transactional_fixtures = false
89
+ fixtures :people, :readers
90
+
91
+ def setup
92
+ # Avoid introspection queries during tests.
93
+ Person.columns; Reader.columns
94
+
95
+ @allow_concurrency = ActiveRecord::Base.allow_concurrency
96
+ ActiveRecord::Base.allow_concurrency = true
97
+ end
98
+
99
+ def teardown
100
+ ActiveRecord::Base.allow_concurrency = @allow_concurrency
101
+ end
102
+
103
+ # Test typical find.
104
+ def test_sane_find_with_lock
105
+ assert_nothing_raised do
106
+ Person.transaction do
107
+ Person.find 1, :lock => true
108
+ end
109
+ end
110
+ end
111
+
112
+ # Test scoped lock.
113
+ def test_sane_find_with_scoped_lock
114
+ assert_nothing_raised do
115
+ Person.transaction do
116
+ Person.with_scope(:find => { :lock => true }) do
117
+ Person.find 1
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ # PostgreSQL protests SELECT ... FOR UPDATE on an outer join.
124
+ unless current_adapter?(:PostgreSQLAdapter)
125
+ # Test locked eager find.
126
+ def test_eager_find_with_lock
127
+ assert_nothing_raised do
128
+ Person.transaction do
129
+ Person.find 1, :include => :readers, :lock => true
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ # Locking a record reloads it.
136
+ def test_sane_lock_method
137
+ assert_nothing_raised do
138
+ Person.transaction do
139
+ person = Person.find 1
140
+ old, person.first_name = person.first_name, 'fooman'
141
+ person.lock!
142
+ assert_equal old, person.first_name
143
+ end
144
+ end
145
+ end
146
+
147
+ if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
148
+ def test_no_locks_no_wait
149
+ first, second = duel { Person.find 1 }
150
+ assert first.end > second.end
151
+ end
152
+
153
+ def test_second_lock_waits
154
+ assert [0.2, 1, 5].any? { |zzz|
155
+ first, second = duel(zzz) { Person.find 1, :lock => true }
156
+ second.end > first.end
157
+ }
158
+ end
159
+
160
+ protected
161
+ def duel(zzz = 5)
162
+ t0, t1, t2, t3 = nil, nil, nil, nil
163
+
164
+ a = Thread.new do
165
+ t0 = Time.now
166
+ Person.transaction do
167
+ yield
168
+ sleep zzz # block thread 2 for zzz seconds
169
+ end
170
+ t1 = Time.now
171
+ end
172
+
173
+ b = Thread.new do
174
+ sleep zzz / 2.0 # ensure thread 1 tx starts first
175
+ t2 = Time.now
176
+ Person.transaction { yield }
177
+ t3 = Time.now
178
+ end
179
+
180
+ a.join
181
+ b.join
182
+
183
+ assert t1 > t0 + zzz
184
+ assert t2 > t0
185
+ assert t3 > t2
186
+ [t0.to_f..t1.to_f, t2.to_f..t3.to_f]
187
+ end
188
+ end
189
+ end
46
190
  end
@@ -57,7 +57,7 @@ class MethodScopingTest < Test::Unit::TestCase
57
57
 
58
58
  Developer.with_scope(:find => { :conditions => 'salary = 100000' }) do
59
59
  assert_equal 8, Developer.count
60
- assert_equal 1, Developer.count("name LIKE 'fixture_1%'")
60
+ assert_equal 1, Developer.count(:conditions => "name LIKE 'fixture_1%'")
61
61
  end
62
62
  end
63
63
 
@@ -74,7 +74,7 @@ class MethodScopingTest < Test::Unit::TestCase
74
74
  def test_scoped_count_include
75
75
  # with the include, will retrieve only developers for the given project
76
76
  Developer.with_scope(:find => { :include => :projects }) do
77
- assert_equal 1, Developer.count('projects.id = 2')
77
+ assert_equal 1, Developer.count(:conditions => 'projects.id = 2')
78
78
  end
79
79
  end
80
80
 
@@ -1,9 +1,15 @@
1
1
  require 'abstract_unit'
2
+ require 'bigdecimal/util'
3
+
2
4
  require 'fixtures/person'
5
+ require 'fixtures/topic'
3
6
  require File.dirname(__FILE__) + '/fixtures/migrations/1_people_have_last_names'
4
7
  require File.dirname(__FILE__) + '/fixtures/migrations/2_we_need_reminders'
8
+ require File.dirname(__FILE__) + '/fixtures/migrations_with_decimal/1_give_me_big_numbers'
9
+
10
+ if ActiveRecord::Base.connection.supports_migrations?
11
+ class BigNumber < ActiveRecord::Base; end
5
12
 
6
- if ActiveRecord::Base.connection.supports_migrations?
7
13
  class Reminder < ActiveRecord::Base; end
8
14
 
9
15
  class ActiveRecord::Migration
@@ -28,37 +34,46 @@ if ActiveRecord::Base.connection.supports_migrations?
28
34
  ActiveRecord::Base.connection.initialize_schema_information
29
35
  ActiveRecord::Base.connection.update "UPDATE #{ActiveRecord::Migrator.schema_info_table_name} SET version = 0"
30
36
 
31
- Reminder.connection.drop_table("reminders") rescue nil
32
- Reminder.connection.drop_table("people_reminders") rescue nil
33
- Reminder.connection.drop_table("prefix_reminders_suffix") rescue nil
37
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
38
+ Reminder.connection.drop_table(table) rescue nil
39
+ end
34
40
  Reminder.reset_column_information
35
41
 
36
- Person.connection.remove_column("people", "last_name") rescue nil
37
- Person.connection.remove_column("people", "key") rescue nil
38
- Person.connection.remove_column("people", "bio") rescue nil
39
- Person.connection.remove_column("people", "age") rescue nil
40
- Person.connection.remove_column("people", "height") rescue nil
41
- Person.connection.remove_column("people", "birthday") rescue nil
42
- Person.connection.remove_column("people", "favorite_day") rescue nil
43
- Person.connection.remove_column("people", "male") rescue nil
44
- Person.connection.remove_column("people", "administrator") rescue nil
42
+ %w(last_name key bio age height wealth birthday favorite_day
43
+ male administrator).each do |column|
44
+ Person.connection.remove_column('people', column) rescue nil
45
+ end
46
+ Person.connection.remove_column("people", "first_name") rescue nil
47
+ Person.connection.remove_column("people", "middle_name") rescue nil
48
+ Person.connection.add_column("people", "first_name", :string, :limit => 40)
45
49
  Person.reset_column_information
46
50
  end
47
51
 
48
52
  def test_add_index
49
- Person.connection.add_column "people", "last_name", :string
53
+ # Limit size of last_name and key columns to support Firebird index limitations
54
+ Person.connection.add_column "people", "last_name", :string, :limit => 100
55
+ Person.connection.add_column "people", "key", :string, :limit => 100
50
56
  Person.connection.add_column "people", "administrator", :boolean
51
- Person.connection.add_column "people", "key", :string
52
-
57
+
53
58
  assert_nothing_raised { Person.connection.add_index("people", "last_name") }
54
59
  assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
55
60
 
56
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
57
- assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
58
-
61
+ # Orcl nds shrt indx nms. Sybs 2.
62
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter)
63
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
64
+ assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
65
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
66
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
67
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
68
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
69
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
70
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
71
+ end
72
+
59
73
  # quoting
60
- assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key", :unique => true) }
61
- assert_nothing_raised { Person.connection.remove_index("people", :name => "key") }
74
+ # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
75
+ assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
76
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
62
77
 
63
78
  # Sybase adapter does not support indexes on :boolean columns
64
79
  unless current_adapter?(:SybaseAdapter)
@@ -79,8 +94,10 @@ if ActiveRecord::Base.connection.supports_migrations?
79
94
  end
80
95
 
81
96
  def test_create_table_with_not_null_column
82
- Person.connection.create_table :testings do |t|
83
- t.column :foo, :string, :null => false
97
+ assert_nothing_raised do
98
+ Person.connection.create_table :testings do |t|
99
+ t.column :foo, :string, :null => false
100
+ end
84
101
  end
85
102
 
86
103
  assert_raises(ActiveRecord::StatementInvalid) do
@@ -105,30 +122,61 @@ if ActiveRecord::Base.connection.supports_migrations?
105
122
  four = columns.detect { |c| c.name == "four" }
106
123
 
107
124
  assert_equal "hello", one.default
108
- if current_adapter?(:OracleAdapter)
109
- # Oracle doesn't support native booleans
110
- assert_equal true, two.default == 1
111
- assert_equal false, three.default != 0
112
- else
113
- assert_equal true, two.default
114
- assert_equal false, three.default
115
- end
125
+ assert_equal true, two.default
126
+ assert_equal false, three.default
116
127
  assert_equal 1, four.default
117
128
 
118
129
  ensure
119
130
  Person.connection.drop_table :testings rescue nil
120
131
  end
121
-
132
+
133
+ def test_create_table_with_limits
134
+ assert_nothing_raised do
135
+ Person.connection.create_table :testings do |t|
136
+ t.column :foo, :string, :limit => 255
137
+
138
+ t.column :default_int, :integer
139
+
140
+ t.column :one_int, :integer, :limit => 1
141
+ t.column :four_int, :integer, :limit => 4
142
+ t.column :eight_int, :integer, :limit => 8
143
+ end
144
+ end
145
+
146
+ columns = Person.connection.columns(:testings)
147
+ foo = columns.detect { |c| c.name == "foo" }
148
+ assert_equal 255, foo.limit
149
+
150
+ default = columns.detect { |c| c.name == "default_int" }
151
+ one = columns.detect { |c| c.name == "one_int" }
152
+ four = columns.detect { |c| c.name == "four_int" }
153
+ eight = columns.detect { |c| c.name == "eight_int" }
154
+
155
+ if current_adapter?(:PostgreSQLAdapter)
156
+ assert_equal 'integer', default.sql_type
157
+ assert_equal 'smallint', one.sql_type
158
+ assert_equal 'integer', four.sql_type
159
+ assert_equal 'bigint', eight.sql_type
160
+ elsif current_adapter?(:OracleAdapter)
161
+ assert_equal 'NUMBER(38)', default.sql_type
162
+ assert_equal 'NUMBER(1)', one.sql_type
163
+ assert_equal 'NUMBER(4)', four.sql_type
164
+ assert_equal 'NUMBER(8)', eight.sql_type
165
+ end
166
+ ensure
167
+ Person.connection.drop_table :testings rescue nil
168
+ end
169
+
122
170
  # SQL Server and Sybase will not allow you to add a NOT NULL column
123
171
  # to a table without specifying a default value, so the
124
- # following test must be skipped
125
- unless current_adapter?(:SQLServerAdapter) || current_adapter?(:SybaseAdapter)
172
+ # following test must be skipped
173
+ unless current_adapter?(:SQLServerAdapter, :SybaseAdapter)
126
174
  def test_add_column_not_null_without_default
127
175
  Person.connection.create_table :testings do |t|
128
176
  t.column :foo, :string
129
177
  end
130
178
  Person.connection.add_column :testings, :bar, :string, :null => false
131
-
179
+
132
180
  assert_raises(ActiveRecord::StatementInvalid) do
133
181
  Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
134
182
  end
@@ -136,52 +184,113 @@ if ActiveRecord::Base.connection.supports_migrations?
136
184
  Person.connection.drop_table :testings rescue nil
137
185
  end
138
186
  end
139
-
187
+
140
188
  def test_add_column_not_null_with_default
141
189
  Person.connection.create_table :testings do |t|
142
190
  t.column :foo, :string
143
191
  end
144
- Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default"
192
+
193
+ con = Person.connection
194
+ Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
195
+ Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
196
+ Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
197
+ assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
145
198
 
146
199
  assert_raises(ActiveRecord::StatementInvalid) do
147
- Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
200
+ Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
148
201
  end
149
202
  ensure
150
203
  Person.connection.drop_table :testings rescue nil
151
204
  end
152
-
205
+
206
+ # We specifically do a manual INSERT here, and then test only the SELECT
207
+ # functionality. This allows us to more easily catch INSERT being broken,
208
+ # but SELECT actually working fine.
209
+ def test_native_decimal_insert_manual_vs_automatic
210
+ # SQLite3 always uses float in violation of SQL
211
+ # 16 decimal places
212
+ correct_value = (current_adapter?(:SQLiteAdapter) ? '0.123456789012346E20' : '0012345678901234567890.0123456789').to_d
213
+
214
+ Person.delete_all
215
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
216
+ Person.reset_column_information
217
+
218
+ # Do a manual insertion
219
+ if current_adapter?(:OracleAdapter)
220
+ Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
221
+ else
222
+ Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
223
+ end
224
+
225
+ # SELECT
226
+ row = Person.find(:first)
227
+ assert_kind_of BigDecimal, row.wealth
228
+
229
+ # If this assert fails, that means the SELECT is broken!
230
+ assert_equal correct_value, row.wealth
231
+
232
+ # Reset to old state
233
+ Person.delete_all
234
+
235
+ # Now use the Rails insertion
236
+ assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
237
+
238
+ # SELECT
239
+ row = Person.find(:first)
240
+ assert_kind_of BigDecimal, row.wealth
241
+
242
+ # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
243
+ assert_equal correct_value, row.wealth
244
+
245
+ # Reset to old state
246
+ Person.connection.del_column "people", "wealth" rescue nil
247
+ Person.reset_column_information
248
+ end
249
+
153
250
  def test_native_types
154
251
  Person.delete_all
155
252
  Person.connection.add_column "people", "last_name", :string
156
253
  Person.connection.add_column "people", "bio", :text
157
254
  Person.connection.add_column "people", "age", :integer
158
255
  Person.connection.add_column "people", "height", :float
256
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
159
257
  Person.connection.add_column "people", "birthday", :datetime
160
258
  Person.connection.add_column "people", "favorite_day", :date
161
259
  Person.connection.add_column "people", "male", :boolean
162
- assert_nothing_raised { Person.create :first_name => 'bob', :last_name => 'bobsen', :bio => "I was born ....", :age => 18, :height => 1.78, :birthday => 18.years.ago, :favorite_day => 10.days.ago, :male => true }
260
+ assert_nothing_raised { Person.create :first_name => 'bob', :last_name => 'bobsen', :bio => "I was born ....", :age => 18, :height => 1.78, :wealth => BigDecimal.new("12345678901234567890.0123456789"), :birthday => 18.years.ago, :favorite_day => 10.days.ago, :male => true }
163
261
  bob = Person.find(:first)
164
-
165
- assert_equal bob.first_name, 'bob'
166
- assert_equal bob.last_name, 'bobsen'
167
- assert_equal bob.bio, "I was born ...."
168
- assert_equal bob.age, 18
169
- assert_equal bob.male?, true
170
-
262
+
263
+ assert_equal 'bob', bob.first_name
264
+ assert_equal 'bobsen', bob.last_name
265
+ assert_equal "I was born ....", bob.bio
266
+ assert_equal 18, bob.age
267
+
268
+ # Test for 30 significent digits (beyond the 16 of float), 10 of them
269
+ # after the decimal place.
270
+ if current_adapter?(:SQLiteAdapter)
271
+ # SQLite3 uses float in violation of SQL. Test for 16 decimal places.
272
+ assert_equal BigDecimal.new('0.123456789012346E20'), bob.wealth
273
+ else
274
+ assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
275
+ end
276
+
277
+ assert_equal true, bob.male?
278
+
171
279
  assert_equal String, bob.first_name.class
172
280
  assert_equal String, bob.last_name.class
173
281
  assert_equal String, bob.bio.class
174
282
  assert_equal Fixnum, bob.age.class
175
283
  assert_equal Time, bob.birthday.class
176
284
 
177
- if current_adapter?(:SQLServerAdapter) || current_adapter?(:OracleAdapter) || current_adapter?(:SybaseAdapter)
178
- # SQL Server, Sybase, and Oracle don't differentiate between date/time
285
+ if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
286
+ # Sybase, and Oracle don't differentiate between date/time
179
287
  assert_equal Time, bob.favorite_day.class
180
288
  else
181
289
  assert_equal Date, bob.favorite_day.class
182
290
  end
183
291
 
184
292
  assert_equal TrueClass, bob.male?.class
293
+ assert_kind_of BigDecimal, bob.wealth
185
294
  end
186
295
 
187
296
  def test_add_remove_single_field_using_string_arguments
@@ -191,7 +300,7 @@ if ActiveRecord::Base.connection.supports_migrations?
191
300
 
192
301
  Person.reset_column_information
193
302
  assert Person.column_methods_hash.include?(:last_name)
194
-
303
+
195
304
  ActiveRecord::Migration.remove_column 'people', 'last_name'
196
305
 
197
306
  Person.reset_column_information
@@ -211,27 +320,27 @@ if ActiveRecord::Base.connection.supports_migrations?
211
320
  Person.reset_column_information
212
321
  assert !Person.column_methods_hash.include?(:last_name)
213
322
  end
214
-
323
+
215
324
  def test_add_rename
216
325
  Person.delete_all
217
-
326
+
218
327
  begin
219
- Person.connection.add_column "people", "girlfriend", :string
220
- Person.create :girlfriend => 'bobette'
221
-
328
+ Person.connection.add_column "people", "girlfriend", :string
329
+ Person.create :girlfriend => 'bobette'
330
+
222
331
  Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
223
-
224
- Person.reset_column_information
332
+
333
+ Person.reset_column_information
225
334
  bob = Person.find(:first)
226
-
335
+
227
336
  assert_equal "bobette", bob.exgirlfriend
228
337
  ensure
229
338
  Person.connection.remove_column("people", "girlfriend") rescue nil
230
339
  Person.connection.remove_column("people", "exgirlfriend") rescue nil
231
340
  end
232
-
341
+
233
342
  end
234
-
343
+
235
344
  def test_rename_column_using_symbol_arguments
236
345
  begin
237
346
  Person.connection.rename_column :people, :first_name, :nick_name
@@ -242,7 +351,7 @@ if ActiveRecord::Base.connection.supports_migrations?
242
351
  Person.connection.add_column("people","first_name", :string)
243
352
  end
244
353
  end
245
-
354
+
246
355
  def test_rename_column
247
356
  begin
248
357
  Person.connection.rename_column "people", "first_name", "nick_name"
@@ -253,7 +362,7 @@ if ActiveRecord::Base.connection.supports_migrations?
253
362
  Person.connection.add_column("people","first_name", :string)
254
363
  end
255
364
  end
256
-
365
+
257
366
  def test_rename_table
258
367
  begin
259
368
  ActiveRecord::Base.connection.create_table :octopuses do |t|
@@ -261,14 +370,11 @@ if ActiveRecord::Base.connection.supports_migrations?
261
370
  end
262
371
  ActiveRecord::Base.connection.rename_table :octopuses, :octopi
263
372
 
264
- assert_nothing_raised do
265
- if current_adapter?(:OracleAdapter)
266
- # Oracle requires the explicit sequence value for the pk
267
- ActiveRecord::Base.connection.execute "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
268
- else
269
- ActiveRecord::Base.connection.execute "INSERT INTO octopi (url) VALUES ('http://www.foreverflying.com/octopus-black7.jpg')"
270
- end
271
- end
373
+ # Using explicit id in insert for compatibility across all databases
374
+ con = ActiveRecord::Base.connection
375
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
376
+ assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
377
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
272
378
 
273
379
  assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
274
380
 
@@ -278,40 +384,159 @@ if ActiveRecord::Base.connection.supports_migrations?
278
384
  end
279
385
  end
280
386
 
387
+ def test_rename_table_with_an_index
388
+ begin
389
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
390
+ t.column :url, :string
391
+ end
392
+ ActiveRecord::Base.connection.add_index :octopuses, :url
393
+
394
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
395
+
396
+ # Using explicit id in insert for compatibility across all databases
397
+ con = ActiveRecord::Base.connection
398
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
399
+ assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
400
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
401
+
402
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
403
+ assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
404
+ ensure
405
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
406
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
407
+ end
408
+ end
409
+
281
410
  def test_change_column
282
411
  Person.connection.add_column 'people', 'age', :integer
283
412
  old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
284
413
  assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
285
414
 
286
415
  assert_nothing_raised { Person.connection.change_column "people", "age", :string }
287
-
416
+
288
417
  new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
289
418
  assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
290
419
  assert new_columns.find { |c| c.name == 'age' and c.type == :string }
291
- end
420
+
421
+ old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
422
+ assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
423
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
424
+ new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
425
+ assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
426
+ assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
427
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
428
+ end
429
+
430
+ def test_change_column_with_nil_default
431
+ Person.connection.add_column "people", "contributor", :boolean, :default => true
432
+ Person.reset_column_information
433
+ assert Person.new.contributor?
434
+
435
+ assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
436
+ Person.reset_column_information
437
+ assert !Person.new.contributor?
438
+ assert_nil Person.new.contributor
439
+ end
292
440
 
293
441
  def test_change_column_with_new_default
294
- Person.connection.add_column "people", "administrator", :boolean, :default => 1
295
- Person.reset_column_information
442
+ Person.connection.add_column "people", "administrator", :boolean, :default => true
443
+ Person.reset_column_information
296
444
  assert Person.new.administrator?
297
-
298
- assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => 0 }
299
- Person.reset_column_information
445
+
446
+ assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
447
+ Person.reset_column_information
300
448
  assert !Person.new.administrator?
301
- end
449
+ end
450
+
451
+ def test_change_column_default
452
+ Person.connection.change_column_default "people", "first_name", "Tester"
453
+ Person.reset_column_information
454
+ assert_equal "Tester", Person.new.first_name
455
+ end
456
+
457
+ def test_change_column_default_to_null
458
+ Person.connection.change_column_default "people", "first_name", nil
459
+ Person.reset_column_information
460
+ assert_nil Person.new.first_name
461
+ end
302
462
 
303
463
  def test_add_table
304
464
  assert !Reminder.table_exists?
305
-
465
+
306
466
  WeNeedReminders.up
307
-
467
+
308
468
  assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
309
469
  assert_equal "hello world", Reminder.find(:first).content
310
-
470
+
311
471
  WeNeedReminders.down
312
472
  assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
313
473
  end
314
474
 
475
+ def test_add_table_with_decimals
476
+ Person.connection.drop_table :big_numbers rescue nil
477
+
478
+ assert !BigNumber.table_exists?
479
+ GiveMeBigNumbers.up
480
+
481
+ assert BigNumber.create(
482
+ :bank_balance => 1586.43,
483
+ :big_bank_balance => BigDecimal("1000234000567.95"),
484
+ :world_population => 6000000000,
485
+ :my_house_population => 3,
486
+ :value_of_e => BigDecimal("2.7182818284590452353602875")
487
+ )
488
+
489
+ b = BigNumber.find(:first)
490
+ assert_not_nil b
491
+
492
+ assert_not_nil b.bank_balance
493
+ assert_not_nil b.big_bank_balance
494
+ assert_not_nil b.world_population
495
+ assert_not_nil b.my_house_population
496
+ assert_not_nil b.value_of_e
497
+
498
+ # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
499
+ # is_a?(Bignum)
500
+ assert_kind_of Integer, b.world_population
501
+ assert_equal 6000000000, b.world_population
502
+ assert_kind_of Fixnum, b.my_house_population
503
+ assert_equal 3, b.my_house_population
504
+ assert_kind_of BigDecimal, b.bank_balance
505
+ assert_equal BigDecimal("1586.43"), b.bank_balance
506
+ assert_kind_of BigDecimal, b.big_bank_balance
507
+ assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
508
+
509
+ # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
510
+ # precision/scale explictly left out. By the SQL standard, numbers
511
+ # assigned to this field should be truncated but that's seldom respected.
512
+ if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
513
+ # - PostgreSQL changes the SQL spec on columns declared simply as
514
+ # "decimal" to something more useful: instead of being given a scale
515
+ # of 0, they take on the compile-time limit for precision and scale,
516
+ # so the following should succeed unless you have used really wacky
517
+ # compilation options
518
+ # - SQLite2 has the default behavior of preserving all data sent in,
519
+ # so this happens there too
520
+ assert_kind_of BigDecimal, b.value_of_e
521
+ assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
522
+ elsif current_adapter?(:SQLiteAdapter)
523
+ # - SQLite3 stores a float, in violation of SQL
524
+ assert_kind_of BigDecimal, b.value_of_e
525
+ assert_equal BigDecimal("2.71828182845905"), b.value_of_e
526
+ elsif current_adapter?(:SQLServer)
527
+ # - SQL Server rounds instead of truncating
528
+ assert_kind_of Fixnum, b.value_of_e
529
+ assert_equal 3, b.value_of_e
530
+ else
531
+ # - SQL standard is an integer
532
+ assert_kind_of Fixnum, b.value_of_e
533
+ assert_equal 2, b.value_of_e
534
+ end
535
+
536
+ GiveMeBigNumbers.down
537
+ assert_raises(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
538
+ end
539
+
315
540
  def test_migrator
316
541
  assert !Person.column_methods_hash.include?(:last_name)
317
542
  assert !Reminder.table_exists?
@@ -335,7 +560,7 @@ if ActiveRecord::Base.connection.supports_migrations?
335
560
  def test_migrator_one_up
336
561
  assert !Person.column_methods_hash.include?(:last_name)
337
562
  assert !Reminder.table_exists?
338
-
563
+
339
564
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
340
565
 
341
566
  Person.reset_column_information
@@ -347,17 +572,17 @@ if ActiveRecord::Base.connection.supports_migrations?
347
572
  assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
348
573
  assert_equal "hello world", Reminder.find(:first).content
349
574
  end
350
-
575
+
351
576
  def test_migrator_one_down
352
577
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
353
-
578
+
354
579
  ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
355
580
 
356
581
  Person.reset_column_information
357
582
  assert Person.column_methods_hash.include?(:last_name)
358
583
  assert !Reminder.table_exists?
359
584
  end
360
-
585
+
361
586
  def test_migrator_one_up_one_down
362
587
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
363
588
  ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
@@ -365,7 +590,7 @@ if ActiveRecord::Base.connection.supports_migrations?
365
590
  assert !Person.column_methods_hash.include?(:last_name)
366
591
  assert !Reminder.table_exists?
367
592
  end
368
-
593
+
369
594
  def test_migrator_verbosity
370
595
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
371
596
  assert PeopleHaveLastNames.message_count > 0
@@ -375,7 +600,7 @@ if ActiveRecord::Base.connection.supports_migrations?
375
600
  assert PeopleHaveLastNames.message_count > 0
376
601
  PeopleHaveLastNames.message_count = 0
377
602
  end
378
-
603
+
379
604
  def test_migrator_verbosity_off
380
605
  PeopleHaveLastNames.verbose = false
381
606
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
@@ -383,7 +608,7 @@ if ActiveRecord::Base.connection.supports_migrations?
383
608
  ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
384
609
  assert PeopleHaveLastNames.message_count.zero?
385
610
  end
386
-
611
+
387
612
  def test_migrator_going_down_due_to_version_target
388
613
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
389
614
  ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
@@ -412,14 +637,14 @@ if ActiveRecord::Base.connection.supports_migrations?
412
637
  ActiveRecord::Base.table_name_prefix = ""
413
638
  ActiveRecord::Base.table_name_suffix = ""
414
639
  end
415
-
640
+
416
641
  def test_proper_table_name
417
642
  assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
418
643
  assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
419
644
  assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
420
645
  Reminder.reset_table_name
421
646
  assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
422
-
647
+
423
648
  # Use the model's own prefix/suffix if a model is given
424
649
  ActiveRecord::Base.table_name_prefix = "ARprefix_"
425
650
  ActiveRecord::Base.table_name_suffix = "_ARsuffix"
@@ -430,8 +655,8 @@ if ActiveRecord::Base.connection.supports_migrations?
430
655
  Reminder.table_name_prefix = ''
431
656
  Reminder.table_name_suffix = ''
432
657
  Reminder.reset_table_name
433
-
434
- # Use AR::Base's prefix/suffix if string or symbol is given
658
+
659
+ # Use AR::Base's prefix/suffix if string or symbol is given
435
660
  ActiveRecord::Base.table_name_prefix = "prefix_"
436
661
  ActiveRecord::Base.table_name_suffix = "_suffix"
437
662
  Reminder.reset_table_name
@@ -440,7 +665,7 @@ if ActiveRecord::Base.connection.supports_migrations?
440
665
  ActiveRecord::Base.table_name_prefix = ""
441
666
  ActiveRecord::Base.table_name_suffix = ""
442
667
  Reminder.reset_table_name
443
- end
668
+ end
444
669
 
445
670
  def test_add_drop_table_with_prefix_and_suffix
446
671
  assert !Reminder.table_exists?
@@ -461,31 +686,83 @@ if ActiveRecord::Base.connection.supports_migrations?
461
686
  Reminder.reset_sequence_name
462
687
  end
463
688
 
464
- def test_create_table_with_binary_column
465
- Person.connection.drop_table :binary_testings rescue nil
466
-
467
- assert_nothing_raised {
468
- Person.connection.create_table :binary_testings do |t|
469
- t.column "data", :binary, :default => "", :null => false
689
+ # FrontBase does not support default values on BLOB/CLOB columns
690
+ unless current_adapter?(:FrontBaseAdapter)
691
+ def test_create_table_with_binary_column
692
+ Person.connection.drop_table :binary_testings rescue nil
693
+
694
+ assert_nothing_raised {
695
+ Person.connection.create_table :binary_testings do |t|
696
+ t.column "data", :binary, :default => "", :null => false
697
+ end
698
+ }
699
+
700
+ columns = Person.connection.columns(:binary_testings)
701
+ data_column = columns.detect { |c| c.name == "data" }
702
+
703
+ if current_adapter?(:OracleAdapter)
704
+ assert_equal "empty_blob()", data_column.default
705
+ else
706
+ assert_equal "", data_column.default
470
707
  end
471
- }
472
-
473
- columns = Person.connection.columns(:binary_testings)
474
- data_column = columns.detect { |c| c.name == "data" }
475
708
 
476
- if current_adapter?(:OracleAdapter)
477
- assert_equal "empty_blob()", data_column.default
478
- else
479
- assert_equal "", data_column.default
709
+ Person.connection.drop_table :binary_testings rescue nil
480
710
  end
481
-
482
- Person.connection.drop_table :binary_testings rescue nil
483
711
  end
484
-
485
712
  def test_migrator_with_duplicates
486
713
  assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
487
714
  ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)
488
715
  end
489
716
  end
717
+
718
+ def test_migrator_with_missing_version_numbers
719
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 500)
720
+ assert !Person.column_methods_hash.include?(:middle_name)
721
+ assert_equal 4, ActiveRecord::Migrator.current_version
722
+
723
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 2)
724
+ assert !Reminder.table_exists?
725
+ assert Person.column_methods_hash.include?(:last_name)
726
+ assert_equal 2, ActiveRecord::Migrator.current_version
727
+ end
728
+
729
+ def test_create_table_with_custom_sequence_name
730
+ return unless current_adapter? :OracleAdapter
731
+
732
+ # table name is 29 chars, the standard sequence name will
733
+ # be 33 chars and fail
734
+ assert_raises(ActiveRecord::StatementInvalid) do
735
+ begin
736
+ Person.connection.create_table :table_with_name_thats_just_ok do |t|
737
+ t.column :foo, :string, :null => false
738
+ end
739
+ ensure
740
+ Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
741
+ end
742
+ end
743
+
744
+ # should be all good w/ a custom sequence name
745
+ assert_nothing_raised do
746
+ begin
747
+ Person.connection.create_table :table_with_name_thats_just_ok,
748
+ :sequence_name => 'suitably_short_seq' do |t|
749
+ t.column :foo, :string, :null => false
750
+ end
751
+
752
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
753
+
754
+ ensure
755
+ Person.connection.drop_table :table_with_name_thats_just_ok,
756
+ :sequence_name => 'suitably_short_seq' rescue nil
757
+ end
758
+ end
759
+
760
+ # confirm the custom sequence got dropped
761
+ assert_raises(ActiveRecord::StatementInvalid) do
762
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
763
+ end
764
+ end
765
+
490
766
  end
491
767
  end
768
+