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.
- data/CHANGELOG +400 -1
- data/README +2 -2
- data/RUNNING_UNIT_TESTS +21 -3
- data/Rakefile +55 -10
- data/lib/active_record.rb +10 -4
- data/lib/active_record/acts/list.rb +15 -4
- data/lib/active_record/acts/nested_set.rb +11 -12
- data/lib/active_record/acts/tree.rb +13 -14
- data/lib/active_record/aggregations.rb +46 -22
- data/lib/active_record/associations.rb +213 -162
- data/lib/active_record/associations/association_collection.rb +45 -15
- data/lib/active_record/associations/association_proxy.rb +32 -13
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +18 -18
- data/lib/active_record/associations/has_many_association.rb +37 -17
- data/lib/active_record/associations/has_many_through_association.rb +120 -30
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +75 -0
- data/lib/active_record/base.rb +282 -203
- data/lib/active_record/calculations.rb +95 -54
- data/lib/active_record/callbacks.rb +13 -24
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +12 -1
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb.rej +21 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +30 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -9
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +121 -37
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +55 -23
- data/lib/active_record/connection_adapters/abstract_adapter.rb +8 -0
- data/lib/active_record/connection_adapters/db2_adapter.rb +1 -11
- data/lib/active_record/connection_adapters/firebird_adapter.rb +364 -50
- data/lib/active_record/connection_adapters/frontbase_adapter.rb +861 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -33
- data/lib/active_record/connection_adapters/openbase_adapter.rb +4 -3
- data/lib/active_record/connection_adapters/oracle_adapter.rb +151 -127
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +125 -48
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +38 -10
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +183 -155
- data/lib/active_record/connection_adapters/sybase_adapter.rb +190 -212
- data/lib/active_record/deprecated_associations.rb +24 -10
- data/lib/active_record/deprecated_finders.rb +4 -1
- data/lib/active_record/fixtures.rb +37 -23
- data/lib/active_record/locking/optimistic.rb +106 -0
- data/lib/active_record/locking/pessimistic.rb +77 -0
- data/lib/active_record/migration.rb +8 -5
- data/lib/active_record/observer.rb +73 -34
- data/lib/active_record/reflection.rb +21 -7
- data/lib/active_record/schema_dumper.rb +33 -5
- data/lib/active_record/timestamp.rb +23 -34
- data/lib/active_record/transactions.rb +37 -30
- data/lib/active_record/validations.rb +46 -30
- data/lib/active_record/vendor/mysql.rb +20 -5
- data/lib/active_record/version.rb +2 -2
- data/lib/active_record/wrappings.rb +1 -2
- data/lib/active_record/xml_serialization.rb +308 -0
- data/test/aaa_create_tables_test.rb +5 -1
- data/test/abstract_unit.rb +18 -8
- data/test/{active_schema_mysql.rb → active_schema_test_mysql.rb} +2 -2
- data/test/adapter_test.rb +9 -7
- data/test/adapter_test_sqlserver.rb +81 -0
- data/test/aggregations_test.rb +29 -0
- data/test/{association_callbacks_test.rb → associations/callbacks_test.rb} +10 -8
- data/test/{associations_cascaded_eager_loading_test.rb → associations/cascaded_eager_loading_test.rb} +35 -3
- data/test/{associations_go_eager_test.rb → associations/eager_test.rb} +36 -2
- data/test/{associations_extensions_test.rb → associations/extension_test.rb} +5 -0
- data/test/{associations_join_model_test.rb → associations/join_model_test.rb} +118 -8
- data/test/associations_test.rb +339 -45
- data/test/attribute_methods_test.rb +49 -0
- data/test/base_test.rb +321 -67
- data/test/calculations_test.rb +48 -10
- data/test/callbacks_test.rb +13 -0
- data/test/connection_test_firebird.rb +8 -0
- data/test/connections/native_db2/connection.rb +18 -17
- data/test/connections/native_firebird/connection.rb +19 -17
- data/test/connections/native_frontbase/connection.rb +27 -0
- data/test/connections/native_mysql/connection.rb +18 -15
- data/test/connections/native_openbase/connection.rb +14 -15
- data/test/connections/native_oracle/connection.rb +16 -12
- data/test/connections/native_postgresql/connection.rb +16 -17
- data/test/connections/native_sqlite/connection.rb +3 -6
- data/test/connections/native_sqlite3/connection.rb +3 -6
- data/test/connections/native_sqlserver/connection.rb +16 -17
- data/test/connections/native_sqlserver_odbc/connection.rb +18 -19
- data/test/connections/native_sybase/connection.rb +16 -17
- data/test/datatype_test_postgresql.rb +52 -0
- data/test/defaults_test.rb +52 -10
- data/test/deprecated_associations_test.rb +151 -107
- data/test/deprecated_finder_test.rb +83 -66
- data/test/empty_date_time_test.rb +25 -0
- data/test/finder_test.rb +118 -11
- data/test/fixtures/accounts.yml +6 -1
- data/test/fixtures/author.rb +27 -4
- data/test/fixtures/categorizations.yml +8 -2
- data/test/fixtures/category.rb +1 -2
- data/test/fixtures/comments.yml +0 -6
- data/test/fixtures/companies.yml +6 -1
- data/test/fixtures/company.rb +23 -1
- data/test/fixtures/company_in_module.rb +8 -10
- data/test/fixtures/customer.rb +2 -2
- data/test/fixtures/customers.yml +9 -0
- data/test/fixtures/db_definitions/db2.drop.sql +1 -0
- data/test/fixtures/db_definitions/db2.sql +9 -0
- data/test/fixtures/db_definitions/firebird.drop.sql +3 -0
- data/test/fixtures/db_definitions/firebird.sql +13 -1
- data/test/fixtures/db_definitions/frontbase.drop.sql +31 -0
- data/test/fixtures/db_definitions/frontbase.sql +262 -0
- data/test/fixtures/db_definitions/frontbase2.drop.sql +1 -0
- data/test/fixtures/db_definitions/frontbase2.sql +4 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
- data/test/fixtures/db_definitions/mysql.sql +23 -14
- data/test/fixtures/db_definitions/openbase.sql +13 -1
- data/test/fixtures/db_definitions/oracle.drop.sql +2 -0
- data/test/fixtures/db_definitions/oracle.sql +29 -2
- data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
- data/test/fixtures/db_definitions/postgresql.sql +13 -3
- data/test/fixtures/db_definitions/schema.rb +29 -1
- data/test/fixtures/db_definitions/sqlite.drop.sql +1 -0
- data/test/fixtures/db_definitions/sqlite.sql +12 -3
- data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
- data/test/fixtures/db_definitions/sqlserver.sql +35 -0
- data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
- data/test/fixtures/db_definitions/sybase.sql +13 -4
- data/test/fixtures/developer.rb +12 -0
- data/test/fixtures/edge.rb +5 -0
- data/test/fixtures/edges.yml +6 -0
- data/test/fixtures/funny_jokes.yml +3 -7
- data/test/fixtures/migrations_with_decimal/1_give_me_big_numbers.rb +15 -0
- data/test/fixtures/migrations_with_missing_versions/1000_people_have_middle_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/1_people_have_last_names.rb +9 -0
- data/test/fixtures/migrations_with_missing_versions/3_we_need_reminders.rb +12 -0
- data/test/fixtures/migrations_with_missing_versions/4_innocent_jointable.rb +12 -0
- data/test/fixtures/mixin.rb +15 -0
- data/test/fixtures/mixins.yml +38 -0
- data/test/fixtures/post.rb +3 -2
- data/test/fixtures/project.rb +3 -1
- data/test/fixtures/topic.rb +6 -1
- data/test/fixtures/topics.yml +4 -4
- data/test/fixtures/vertex.rb +9 -0
- data/test/fixtures/vertices.yml +4 -0
- data/test/fixtures_test.rb +45 -0
- data/test/inheritance_test.rb +67 -6
- data/test/lifecycle_test.rb +40 -19
- data/test/locking_test.rb +170 -26
- data/test/method_scoping_test.rb +2 -2
- data/test/migration_test.rb +387 -110
- data/test/migration_test_firebird.rb +124 -0
- data/test/mixin_nested_set_test.rb +14 -2
- data/test/mixin_test.rb +56 -18
- data/test/modules_test.rb +8 -2
- data/test/multiple_db_test.rb +2 -2
- data/test/pk_test.rb +1 -0
- data/test/reflection_test.rb +8 -2
- data/test/schema_authorization_test_postgresql.rb +75 -0
- data/test/schema_dumper_test.rb +40 -4
- data/test/table_name_test_sqlserver.rb +23 -0
- data/test/threaded_connections_test.rb +19 -16
- data/test/transactions_test.rb +86 -72
- data/test/validations_test.rb +126 -56
- data/test/xml_serialization_test.rb +125 -0
- metadata +45 -11
- data/lib/active_record/locking.rb +0 -79
data/test/lifecycle_test.rb
CHANGED
@@ -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
|
100
|
+
topic = Topic.find(1)
|
101
|
+
assert_equal topic.title, topic_observer.topic.title
|
91
102
|
end
|
92
|
-
|
93
|
-
def
|
103
|
+
|
104
|
+
def test_inferred_auto_observer
|
94
105
|
topic_observer = TopicObserver.instance
|
95
106
|
|
96
|
-
topic = Topic.find(1)
|
97
|
-
assert_equal
|
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
|
115
|
+
assert_equal topic.title, multi_observer.record.title
|
105
116
|
|
106
|
-
developer = Developer.find(1)
|
107
|
-
assert_equal
|
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
|
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
|
data/test/locking_test.rb
CHANGED
@@ -2,45 +2,189 @@ require 'abstract_unit'
|
|
2
2
|
require 'fixtures/person'
|
3
3
|
require 'fixtures/legacy_thing'
|
4
4
|
|
5
|
-
class
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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.
|
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
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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.
|
38
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
data/test/method_scoping_test.rb
CHANGED
@@ -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
|
|
data/test/migration_test.rb
CHANGED
@@ -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
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
Person.connection.remove_column("people", "
|
41
|
-
Person.connection.remove_column("people", "
|
42
|
-
Person.connection.
|
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
|
-
|
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
|
-
|
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
|
-
|
57
|
-
|
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
|
-
|
61
|
-
assert_nothing_raised { Person.connection.
|
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
|
-
|
83
|
-
|
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
|
-
|
109
|
-
|
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
|
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
|
-
|
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
|
166
|
-
assert_equal bob.last_name
|
167
|
-
assert_equal
|
168
|
-
assert_equal bob.age
|
169
|
-
|
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
|
178
|
-
#
|
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
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
-
|
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 =>
|
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 =>
|
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
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
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
|
-
|
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
|
+
|