ibm_db 2.5.6-x86-mswin32-60

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/CHANGES +181 -0
  2. data/LICENSE +18 -0
  3. data/MANIFEST +14 -0
  4. data/ParameterizedQueries README +39 -0
  5. data/README +282 -0
  6. data/ext/Makefile.nt32 +181 -0
  7. data/ext/extconf.rb +66 -0
  8. data/ext/ibm_db.c +11166 -0
  9. data/ext/ruby_ibm_db.h +236 -0
  10. data/ext/ruby_ibm_db_cli.c +738 -0
  11. data/ext/ruby_ibm_db_cli.h +431 -0
  12. data/init.rb +42 -0
  13. data/lib/IBM_DB.rb +2 -0
  14. data/lib/active_record/connection_adapters/ibm_db_adapter.rb +2598 -0
  15. data/lib/active_record/connection_adapters/ibm_db_pstmt.rb +1965 -0
  16. data/lib/active_record/vendor/db2-i5-zOS.yaml +328 -0
  17. data/lib/mswin32/ibm_db.rb +1 -0
  18. data/lib/mswin32/rb18x/ibm_db.so +0 -0
  19. data/lib/mswin32/rb19x/ibm_db.so +0 -0
  20. data/test/cases/adapter_test.rb +202 -0
  21. data/test/cases/associations/belongs_to_associations_test.rb +486 -0
  22. data/test/cases/associations/cascaded_eager_loading_test.rb +183 -0
  23. data/test/cases/associations/eager_test.rb +862 -0
  24. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +917 -0
  25. data/test/cases/associations/has_many_through_associations_test.rb +461 -0
  26. data/test/cases/associations/join_model_test.rb +793 -0
  27. data/test/cases/attribute_methods_test.rb +621 -0
  28. data/test/cases/base_test.rb +1486 -0
  29. data/test/cases/calculations_test.rb +362 -0
  30. data/test/cases/finder_test.rb +1088 -0
  31. data/test/cases/fixtures_test.rb +684 -0
  32. data/test/cases/migration_test.rb +2014 -0
  33. data/test/cases/schema_dumper_test.rb +232 -0
  34. data/test/cases/validations/uniqueness_validation_test.rb +283 -0
  35. data/test/connections/native_ibm_db/connection.rb +42 -0
  36. data/test/ibm_db_test.rb +25 -0
  37. data/test/models/warehouse_thing.rb +5 -0
  38. data/test/schema/i5/ibm_db_specific_schema.rb +135 -0
  39. data/test/schema/ids/ibm_db_specific_schema.rb +138 -0
  40. data/test/schema/luw/ibm_db_specific_schema.rb +135 -0
  41. data/test/schema/schema.rb +647 -0
  42. data/test/schema/zOS/ibm_db_specific_schema.rb +206 -0
  43. metadata +107 -0
@@ -0,0 +1,2014 @@
1
+ require "cases/helper"
2
+ require 'bigdecimal/util'
3
+
4
+ require 'models/person'
5
+ require 'models/topic'
6
+ require 'models/developer'
7
+
8
+ require MIGRATIONS_ROOT + "/valid/1_people_have_last_names"
9
+ require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
10
+ require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
11
+ require MIGRATIONS_ROOT + "/interleaved/pass_3/2_i_raise_on_down"
12
+
13
+ if ActiveRecord::Base.connection.supports_migrations?
14
+ class BigNumber < ActiveRecord::Base; end
15
+
16
+ class Reminder < ActiveRecord::Base; end
17
+
18
+ class ActiveRecord::Migration
19
+ class <<self
20
+ attr_accessor :message_count
21
+ def puts(text="")
22
+ self.message_count ||= 0
23
+ self.message_count += 1
24
+ end
25
+ end
26
+ end
27
+
28
+ class MigrationTableAndIndexTest < ActiveRecord::TestCase
29
+ def test_add_schema_info_respects_prefix_and_suffix
30
+ conn = ActiveRecord::Base.connection
31
+
32
+ conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
33
+ # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
34
+ ActiveRecord::Base.table_name_prefix = 'p_'
35
+ ActiveRecord::Base.table_name_suffix = '_s'
36
+ conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
37
+
38
+ conn.initialize_schema_migrations_table
39
+
40
+ assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name]
41
+ ensure
42
+ ActiveRecord::Base.table_name_prefix = ""
43
+ ActiveRecord::Base.table_name_suffix = ""
44
+ end
45
+ end
46
+
47
+ class MigrationTest < ActiveRecord::TestCase
48
+ self.use_transactional_fixtures = false
49
+ if (current_adapter?(:IBM_DBAdapter))
50
+ #Rename is supported only for server zOS 9 , DB2 COBRA and Informix
51
+ server_type = ActiveRecord::Base.connection.servertype.class.name
52
+ @ibm_db_rename_supported = server_type.include?('::IBM_DB2_LUW_COBRA') ||
53
+ server_type.class.name.include?('::IBM_IDS') ||
54
+ (server_type.include?('IBM_DB2_ZOS') && !server_type.include?('IBM_DB2_ZOS_8'))
55
+ end
56
+
57
+ fixtures :people
58
+
59
+ def setup
60
+ ActiveRecord::Migration.verbose = true
61
+ PeopleHaveLastNames.message_count = 0
62
+ end
63
+
64
+ def teardown
65
+ ActiveRecord::Base.connection.initialize_schema_migrations_table
66
+ ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
67
+
68
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
69
+ Reminder.connection.drop_table(table) rescue nil
70
+ end
71
+ Reminder.reset_column_information
72
+
73
+ %w(last_name key bio age height wealth birthday favorite_day
74
+ moment_of_truth male administrator funny).each do |column|
75
+ Person.connection.remove_column('people', column) rescue nil
76
+ end
77
+ Person.connection.remove_column("people", "first_name") rescue nil
78
+ Person.connection.remove_column("people", "middle_name") rescue nil
79
+ Person.connection.add_column("people", "first_name", :string, :limit => 40)
80
+ Person.reset_column_information
81
+ end
82
+
83
+ def test_add_index
84
+ # Limit size of last_name and key columns to support Firebird index limitations
85
+ Person.connection.add_column "people", "last_name", :string, :limit => 100
86
+ Person.connection.add_column "people", "key", :string, :limit => 100
87
+ Person.connection.add_column "people", "administrator", :boolean
88
+
89
+ assert_nothing_raised { Person.connection.add_index("people", "last_name") }
90
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
91
+
92
+ # Orcl nds shrt indx nms. Sybs 2.
93
+ # OpenBase does not have named indexes. You must specify a single column name
94
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
95
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
96
+ assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
97
+ # Oracle adapter cannot have specified index name larger than 30 characters
98
+ # Oracle adapter is shortening index name when just column list is given
99
+ unless current_adapter?(:OracleAdapter)
100
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
101
+ assert_nothing_raised { Person.connection.remove_index("people", :name => :index_people_on_last_name_and_first_name) }
102
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
103
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
104
+ end
105
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
106
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
107
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) }
108
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
109
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) }
110
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) }
111
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) }
112
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
113
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) }
114
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
115
+ end
116
+
117
+ # quoting
118
+ # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
119
+ # OpenBase does not have named indexes. You must specify a single column name
120
+ unless current_adapter?(:OpenBaseAdapter)
121
+ unless current_adapter?(:IBM_DBAdapter) #cannot assign a integer value to string column
122
+ Person.update_all "#{Person.connection.quote_column_name 'key'}=#{Person.connection.quote_column_name 'id'}" #some databases (including sqlite2 won't add a unique index if existing data non unique)
123
+ else
124
+ Person.update_all "#{Person.connection.quote_column_name 'key'}=#{Person.connection.quote_column_name 'first_name'}"
125
+ end
126
+ assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
127
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
128
+ end
129
+
130
+ # Sybase adapter does not support indexes on :boolean columns
131
+ # OpenBase does not have named indexes. You must specify a single column
132
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
133
+ assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
134
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
135
+ end
136
+ end
137
+
138
+ def test_index_symbol_names
139
+ assert_nothing_raised { Person.connection.add_index :people, :primary_contact_id, :name => :symbol_index_name }
140
+ assert Person.connection.index_exists?(:people, :primary_contact_id, :name => :symbol_index_name)
141
+ assert_nothing_raised { Person.connection.remove_index :people, :name => :symbol_index_name }
142
+ assert !Person.connection.index_exists?(:people, :primary_contact_id, :name => :symbol_index_name)
143
+ end
144
+
145
+ def test_add_index_length_limit
146
+ good_index_name = 'x' * Person.connection.index_name_length
147
+ too_long_index_name = good_index_name + 'x'
148
+ assert_raise(ArgumentError) { Person.connection.add_index("people", "first_name", :name => too_long_index_name) }
149
+ assert !Person.connection.index_name_exists?("people", too_long_index_name, false)
150
+ assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) }
151
+ assert Person.connection.index_name_exists?("people", good_index_name, false)
152
+ Person.connection.remove_index("people", :name => good_index_name)
153
+ end
154
+
155
+ def test_remove_nonexistent_index
156
+ # we do this by name, so OpenBase is a wash as noted above
157
+ unless current_adapter?(:OpenBaseAdapter)
158
+ assert_raise(ArgumentError) { Person.connection.remove_index("people", "no_such_index") }
159
+ end
160
+ end
161
+
162
+ def test_rename_index
163
+ unless current_adapter?(:OpenBaseAdapter)
164
+ # keep the names short to make Oracle and similar behave
165
+ Person.connection.add_index('people', [:first_name], :name => 'old_idx')
166
+ assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_idx') }
167
+ # if the adapter doesn't support the indexes call, pick defaults that let the test pass
168
+ assert !Person.connection.index_name_exists?('people', 'old_idx', false)
169
+ assert Person.connection.index_name_exists?('people', 'new_idx', true)
170
+ end
171
+ end
172
+
173
+ def test_double_add_index
174
+ unless current_adapter?(:OpenBaseAdapter)
175
+ Person.connection.add_index('people', [:first_name], :name => 'some_idx')
176
+ assert_raise(ArgumentError) { Person.connection.add_index('people', [:first_name], :name => 'some_idx') }
177
+ end
178
+ end
179
+
180
+ def test_index_exists
181
+ Person.connection.create_table :testings do |t|
182
+ t.column :foo, :string, :limit => 100
183
+ t.column :bar, :string, :limit => 100
184
+ end
185
+ Person.connection.add_index :testings, :foo
186
+
187
+ assert Person.connection.index_exists?(:testings, :foo)
188
+ assert !Person.connection.index_exists?(:testings, :bar)
189
+ ensure
190
+ Person.connection.drop_table :testings rescue nil
191
+ end
192
+
193
+ def test_index_exists_on_multiple_columns
194
+ Person.connection.create_table :testings do |t|
195
+ t.column :foo, :string, :limit => 100
196
+ t.column :bar, :string, :limit => 100
197
+ end
198
+ Person.connection.add_index :testings, [:foo, :bar]
199
+
200
+ assert Person.connection.index_exists?(:testings, [:foo, :bar])
201
+ ensure
202
+ Person.connection.drop_table :testings rescue nil
203
+ end
204
+
205
+ def test_unique_index_exists
206
+ Person.connection.create_table :testings do |t|
207
+ t.column :foo, :string, :limit => 100
208
+ end
209
+ Person.connection.add_index :testings, :foo, :unique => true
210
+
211
+ assert Person.connection.index_exists?(:testings, :foo, :unique => true)
212
+ ensure
213
+ Person.connection.drop_table :testings rescue nil
214
+ end
215
+
216
+ def test_named_index_exists
217
+ Person.connection.create_table :testings do |t|
218
+ t.column :foo, :string, :limit => 100
219
+ end
220
+ Person.connection.add_index :testings, :foo, :name => "custom_index_name"
221
+
222
+ assert Person.connection.index_exists?(:testings, :foo, :name => "custom_index_name")
223
+ ensure
224
+ Person.connection.drop_table :testings rescue nil
225
+ end
226
+
227
+ def testing_table_with_only_foo_attribute
228
+ Person.connection.create_table :testings, :id => false do |t|
229
+ t.column :foo, :string
230
+ end
231
+
232
+ yield Person.connection
233
+ ensure
234
+ Person.connection.drop_table :testings rescue nil
235
+ end
236
+ protected :testing_table_with_only_foo_attribute
237
+
238
+ def test_create_table_without_id
239
+ testing_table_with_only_foo_attribute do |connection|
240
+ assert_equal connection.columns(:testings).size, 1
241
+ end
242
+ end
243
+
244
+ unless current_adapter?(:IBM_DBAdapter)
245
+ # Cannot add a primary key to a table with some rows already in it as it violates the unique constraint
246
+ # Secondly GENERATED BY DEFAULT AS IDENTITY cannot be applied in a alter table command.
247
+ # as this will be wrong sql syntax for DB2
248
+ def test_add_column_with_primary_key_attribute
249
+ testing_table_with_only_foo_attribute do |connection|
250
+ assert_nothing_raised { connection.add_column :testings, :id, :primary_key }
251
+ assert_equal connection.columns(:testings).size, 2
252
+ end
253
+ end
254
+ end
255
+
256
+ def test_create_table_adds_id
257
+ Person.connection.create_table :testings do |t|
258
+ t.column :foo, :string
259
+ end
260
+
261
+ assert_equal %w(foo id),
262
+ Person.connection.columns(:testings).map { |c| c.name }.sort
263
+ ensure
264
+ Person.connection.drop_table :testings rescue nil
265
+ end
266
+
267
+ def test_create_table_with_not_null_column
268
+ assert_nothing_raised do
269
+ Person.connection.create_table :testings do |t|
270
+ t.column :foo, :string, :null => false
271
+ end
272
+ end
273
+
274
+ assert_raise(ActiveRecord::StatementInvalid) do
275
+ Person.connection.execute "insert into testings (foo) values (NULL)"
276
+ end
277
+ ensure
278
+ Person.connection.drop_table :testings rescue nil
279
+ end
280
+
281
+ def test_create_table_with_defaults
282
+ # MySQL doesn't allow defaults on TEXT or BLOB columns.
283
+ mysql = current_adapter?(:MysqlAdapter)
284
+ ibm_ids_zOS = ActiveRecord::Base.connection.servertype.class.name.include?('::IBM_IDS')||
285
+ ActiveRecord::Base.connection.servertype.class.name.include?('::IBM_DB2_ZOS')
286
+
287
+ Person.connection.create_table :testings do |t|
288
+ t.column :one, :string, :default => "hello"
289
+ t.column :two, :boolean, :default => true
290
+ t.column :three, :boolean, :default => false
291
+ t.column :four, :integer, :default => 1
292
+ t.column :five, :text, :default => "hello" unless mysql || ibm_ids_zOS
293
+ end
294
+
295
+ columns = Person.connection.columns(:testings)
296
+ one = columns.detect { |c| c.name == "one" }
297
+ two = columns.detect { |c| c.name == "two" }
298
+ three = columns.detect { |c| c.name == "three" }
299
+ four = columns.detect { |c| c.name == "four" }
300
+ five = columns.detect { |c| c.name == "five" } unless mysql || ibm_ids_zOS
301
+
302
+ assert_equal "hello", one.default
303
+ assert_equal true, two.default
304
+ assert_equal false, three.default
305
+ assert_equal 1, four.default
306
+ assert_equal "hello", five.default unless mysql || ibm_ids_zOS
307
+
308
+ ensure
309
+ Person.connection.drop_table :testings rescue nil
310
+ end
311
+
312
+ if current_adapter?(:IBM_DBAdapter)
313
+ def test_no_limits_datatypes_IBM_DB
314
+ ibm_ids = ActiveRecord::Base.connection.servertype.class.name.include?('::IBM_IDS')
315
+ clasz = Class.new(ActiveRecord::Base)
316
+ clasz.table_name = 'test_no_limits_datatypes_IBM_DB'
317
+ assert_nothing_raised do
318
+ clasz.connection.create_table clasz.table_name do |t|
319
+ t.column "test_varchar", :string, :limit => 10
320
+ t.column "test_integer", :integer, :limit => 5
321
+ t.column "test_boolean", :boolean, :limit => 5
322
+ t.column "test_double", :double, :limit => 10
323
+ t.column "test_date", :date, :limit => 10
324
+ t.column "test_time", :time, :limit => 10
325
+ t.column "test_tstamp", :timestamp, :limit => 10
326
+ t.column "test_xml", :xml, :limit => 10 unless ibm_ids
327
+ t.column "test_clob", :text, :limit => 10000
328
+ t.column "test_decfloat", :decfloat, :limit => 100
329
+ end
330
+ end
331
+ ensure
332
+ clasz.connection.drop_table(clasz.table_name) rescue nil
333
+ end
334
+
335
+ #Sexy migration test for column of type xml and char
336
+ def test_short_hand_migrations_for_ibm_db_datatypes
337
+ ibm_ids = ActiveRecord::Base.connection.servertype.class.name.include?('::IBM_IDS')
338
+ clasz = Class.new(ActiveRecord::Base)
339
+ clasz.table_name = 'test_short_hand_migrations'
340
+ assert_nothing_raised do
341
+ clasz.connection.create_table clasz.table_name do |t|
342
+ t.xml :xml_col unless ibm_ids
343
+ t.char :char_col, :limit=>10
344
+ t.decfloat :dec_col, :precision=>16
345
+ end
346
+ end
347
+ assert_nothing_raised do
348
+ clasz.connection.change_table clasz.table_name do |t|
349
+ t.xml :xml_col1 unless ibm_ids
350
+ t.char :char_col1, :limit=>50
351
+ t.decfloat :dec_col1, :precision=>34
352
+ end
353
+ end
354
+ ensure
355
+ clasz.connection.drop_table(clasz.table_name) rescue nil
356
+ end
357
+ end
358
+
359
+ def test_create_table_with_limits
360
+ assert_nothing_raised do
361
+ Person.connection.create_table :testings do |t|
362
+ t.column :foo, :string, :limit => 255
363
+
364
+ t.column :default_int, :integer
365
+
366
+ t.column :one_int, :integer, :limit => 1
367
+ t.column :four_int, :integer, :limit => 4
368
+ t.column :eight_int, :integer, :limit => 8
369
+ t.column :eleven_int, :integer, :limit => 11
370
+ end
371
+ end
372
+
373
+ columns = Person.connection.columns(:testings)
374
+ foo = columns.detect { |c| c.name == "foo" }
375
+ assert_equal 255, foo.limit
376
+
377
+ default = columns.detect { |c| c.name == "default_int" }
378
+ one = columns.detect { |c| c.name == "one_int" }
379
+ four = columns.detect { |c| c.name == "four_int" }
380
+ eight = columns.detect { |c| c.name == "eight_int" }
381
+ eleven = columns.detect { |c| c.name == "eleven_int" }
382
+
383
+ if current_adapter?(:PostgreSQLAdapter)
384
+ assert_equal 'integer', default.sql_type
385
+ assert_equal 'smallint', one.sql_type
386
+ assert_equal 'integer', four.sql_type
387
+ assert_equal 'bigint', eight.sql_type
388
+ assert_equal 'integer', eleven.sql_type
389
+ elsif current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
390
+ assert_match 'int(11)', default.sql_type
391
+ assert_match 'tinyint', one.sql_type
392
+ assert_match 'int', four.sql_type
393
+ assert_match 'bigint', eight.sql_type
394
+ assert_match 'int(11)', eleven.sql_type
395
+ elsif current_adapter?(:OracleAdapter)
396
+ assert_equal 'NUMBER(38)', default.sql_type
397
+ assert_equal 'NUMBER(1)', one.sql_type
398
+ assert_equal 'NUMBER(4)', four.sql_type
399
+ assert_equal 'NUMBER(8)', eight.sql_type
400
+ end
401
+ ensure
402
+ Person.connection.drop_table :testings rescue nil
403
+ end
404
+
405
+ def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
406
+ ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
407
+
408
+ Person.connection.create_table :testings do |t|
409
+ t.column :foo, :string
410
+ end
411
+
412
+ assert_equal %w(foo testing_id), Person.connection.columns(:testings).map { |c| c.name }.sort
413
+ ensure
414
+ Person.connection.drop_table :testings rescue nil
415
+ ActiveRecord::Base.primary_key_prefix_type = nil
416
+ end
417
+
418
+ def test_create_table_with_primary_key_prefix_as_table_name
419
+ ActiveRecord::Base.primary_key_prefix_type = :table_name
420
+
421
+ Person.connection.create_table :testings do |t|
422
+ t.column :foo, :string
423
+ end
424
+
425
+ assert_equal %w(foo testingid), Person.connection.columns(:testings).map { |c| c.name }.sort
426
+ ensure
427
+ Person.connection.drop_table :testings rescue nil
428
+ ActiveRecord::Base.primary_key_prefix_type = nil
429
+ end
430
+
431
+ def test_create_table_with_force_true_does_not_drop_nonexisting_table
432
+ if Person.connection.table_exists?(:testings2)
433
+ Person.connection.drop_table :testings2
434
+ end
435
+
436
+ # using a copy as we need the drop_table method to
437
+ # continue to work for the ensure block of the test
438
+ temp_conn = Person.connection.dup
439
+ temp_conn.expects(:drop_table).never
440
+ temp_conn.create_table :testings2, :force => true do |t|
441
+ t.column :foo, :string
442
+ end
443
+ ensure
444
+ Person.connection.drop_table :testings2 rescue nil
445
+ end
446
+
447
+ def test_create_table_with_timestamps_should_create_datetime_columns
448
+ table_name = :testings
449
+
450
+ Person.connection.create_table table_name do |t|
451
+ t.timestamps
452
+ end
453
+ created_columns = Person.connection.columns(table_name)
454
+
455
+ created_at_column = created_columns.detect {|c| c.name == 'created_at' }
456
+ updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
457
+
458
+ assert created_at_column.null
459
+ assert updated_at_column.null
460
+ ensure
461
+ Person.connection.drop_table table_name rescue nil
462
+ end
463
+
464
+ def test_create_table_with_timestamps_should_create_datetime_columns_with_options
465
+ table_name = :testings
466
+
467
+ Person.connection.create_table table_name do |t|
468
+ t.timestamps :null => false
469
+ end
470
+ created_columns = Person.connection.columns(table_name)
471
+
472
+ created_at_column = created_columns.detect {|c| c.name == 'created_at' }
473
+ updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
474
+
475
+ assert !created_at_column.null
476
+ assert !updated_at_column.null
477
+ ensure
478
+ Person.connection.drop_table table_name rescue nil
479
+ end
480
+
481
+ def test_create_table_without_a_block
482
+ table_name = :testings
483
+ Person.connection.create_table table_name
484
+ ensure
485
+ Person.connection.drop_table table_name rescue nil
486
+ end
487
+
488
+ # Sybase, and SQLite3 will not allow you to add a NOT NULL
489
+ # column to a table without a default value.
490
+ unless current_adapter?(:SybaseAdapter, :SQLiteAdapter, :IBM_DBAdapter)
491
+ def test_add_column_not_null_without_default
492
+ Person.connection.create_table :testings do |t|
493
+ t.column :foo, :string
494
+ end
495
+ Person.connection.add_column :testings, :bar, :string, :null => false
496
+
497
+ assert_raise(ActiveRecord::StatementInvalid) do
498
+ Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
499
+ end
500
+ ensure
501
+ Person.connection.drop_table :testings rescue nil
502
+ end
503
+ end
504
+
505
+ def test_add_column_not_null_with_default
506
+ Person.connection.create_table :testings do |t|
507
+ t.column :foo, :string
508
+ end
509
+
510
+ con = Person.connection
511
+ Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
512
+ Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
513
+ Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
514
+ assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
515
+
516
+ assert_raise(ActiveRecord::StatementInvalid) do
517
+ unless current_adapter?(:OpenBaseAdapter)
518
+ 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)"
519
+ else
520
+ Person.connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
521
+ "Testing Insert","id",2)
522
+ end
523
+ end
524
+ ensure
525
+ Person.connection.drop_table :testings rescue nil
526
+ end
527
+
528
+ # We specifically do a manual INSERT here, and then test only the SELECT
529
+ # functionality. This allows us to more easily catch INSERT being broken,
530
+ # but SELECT actually working fine.
531
+ def test_native_decimal_insert_manual_vs_automatic
532
+ correct_value = '0012345678901234567890.0123456789'.to_d
533
+
534
+ Person.delete_all
535
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
536
+ Person.reset_column_information
537
+
538
+ # Do a manual insertion
539
+ if current_adapter?(:OracleAdapter)
540
+ Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
541
+ elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
542
+ Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
543
+ else
544
+ Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
545
+ end
546
+
547
+ # SELECT
548
+ row = Person.find(:first)
549
+ assert_kind_of BigDecimal, row.wealth
550
+
551
+ # If this assert fails, that means the SELECT is broken!
552
+ unless current_adapter?(:SQLite3Adapter)
553
+ assert_equal correct_value, row.wealth
554
+ end
555
+
556
+ # Reset to old state
557
+ Person.delete_all
558
+
559
+ # Now use the Rails insertion
560
+ assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
561
+
562
+ # SELECT
563
+ row = Person.find(:first)
564
+ assert_kind_of BigDecimal, row.wealth
565
+
566
+ # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
567
+ unless current_adapter?(:SQLite3Adapter)
568
+ assert_equal correct_value, row.wealth
569
+ end
570
+
571
+ # Reset to old state
572
+ Person.connection.del_column "people", "wealth" rescue nil
573
+ Person.reset_column_information
574
+ end
575
+
576
+ def test_add_column_with_precision_and_scale
577
+ Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
578
+ Person.reset_column_information
579
+
580
+ wealth_column = Person.columns_hash['wealth']
581
+ assert_equal 9, wealth_column.precision
582
+ assert_equal 7, wealth_column.scale
583
+ end
584
+
585
+ def test_native_types
586
+ Person.delete_all
587
+ Person.connection.add_column "people", "last_name", :string
588
+ Person.connection.add_column "people", "bio", :text
589
+ Person.connection.add_column "people", "age", :integer
590
+ Person.connection.add_column "people", "height", :float
591
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
592
+ Person.connection.add_column "people", "birthday", :datetime
593
+ Person.connection.add_column "people", "favorite_day", :date
594
+ Person.connection.add_column "people", "moment_of_truth", :datetime
595
+ Person.connection.add_column "people", "male", :boolean
596
+ Person.reset_column_information
597
+
598
+ assert_nothing_raised do
599
+ Person.create :first_name => 'bob', :last_name => 'bobsen',
600
+ :bio => "I was born ....", :age => 18, :height => 1.78,
601
+ :wealth => BigDecimal.new("12345678901234567890.0123456789"),
602
+ :birthday => 18.years.ago, :favorite_day => 10.days.ago,
603
+ :moment_of_truth => "1782-10-10 21:40:18", :male => true
604
+ end
605
+
606
+ bob = Person.find(:first)
607
+ assert_equal 'bob', bob.first_name
608
+ assert_equal 'bobsen', bob.last_name
609
+ assert_equal "I was born ....", bob.bio
610
+ assert_equal 18, bob.age
611
+
612
+ # Test for 30 significent digits (beyond the 16 of float), 10 of them
613
+ # after the decimal place.
614
+
615
+ unless current_adapter?(:SQLite3Adapter)
616
+ assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
617
+ end
618
+
619
+ assert_equal true, bob.male?
620
+
621
+ assert_equal String, bob.first_name.class
622
+ assert_equal String, bob.last_name.class
623
+ assert_equal String, bob.bio.class
624
+ assert_equal Fixnum, bob.age.class
625
+ assert_equal Time, bob.birthday.class
626
+
627
+ if current_adapter?(:OracleAdapter, :SybaseAdapter)
628
+ # Sybase, and Oracle don't differentiate between date/time
629
+ assert_equal Time, bob.favorite_day.class
630
+ else
631
+ assert_equal Date, bob.favorite_day.class
632
+ end
633
+
634
+ # Oracle adapter stores Time or DateTime with timezone value already in _before_type_cast column
635
+ # therefore no timezone change is done afterwards when default timezone is changed
636
+ unless current_adapter?(:OracleAdapter)
637
+ # Test DateTime column and defaults, including timezone.
638
+ # FIXME: moment of truth may be Time on 64-bit platforms.
639
+ if bob.moment_of_truth.is_a?(DateTime)
640
+
641
+ with_env_tz 'US/Eastern' do
642
+ bob.reload
643
+ assert_equal DateTime.local_offset, bob.moment_of_truth.offset
644
+ assert_not_equal 0, bob.moment_of_truth.offset
645
+ assert_not_equal "Z", bob.moment_of_truth.zone
646
+ # US/Eastern is -5 hours from GMT
647
+ assert_equal Rational(-5, 24), bob.moment_of_truth.offset
648
+ assert_match(/\A-05:?00\Z/, bob.moment_of_truth.zone) #ruby 1.8.6 uses HH:MM, prior versions use HHMM
649
+ assert_equal DateTime::ITALY, bob.moment_of_truth.start
650
+ end
651
+ end
652
+ end
653
+
654
+ assert_instance_of TrueClass, bob.male?
655
+ assert_kind_of BigDecimal, bob.wealth
656
+ end
657
+
658
+ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
659
+ def test_unabstracted_database_dependent_types
660
+ Person.delete_all
661
+
662
+ ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
663
+ Person.reset_column_information
664
+ assert_match(/tinyint/, Person.columns_hash['intelligence_quotient'].sql_type)
665
+ ensure
666
+ ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
667
+ end
668
+ end
669
+
670
+ def test_add_remove_single_field_using_string_arguments
671
+ assert !Person.column_methods_hash.include?(:last_name)
672
+
673
+ ActiveRecord::Migration.add_column 'people', 'last_name', :string
674
+
675
+ Person.reset_column_information
676
+ assert Person.column_methods_hash.include?(:last_name)
677
+
678
+ ActiveRecord::Migration.remove_column 'people', 'last_name'
679
+
680
+ Person.reset_column_information
681
+ assert !Person.column_methods_hash.include?(:last_name)
682
+ end
683
+
684
+ def test_add_remove_single_field_using_symbol_arguments
685
+ assert !Person.column_methods_hash.include?(:last_name)
686
+
687
+ ActiveRecord::Migration.add_column :people, :last_name, :string
688
+
689
+ Person.reset_column_information
690
+ assert Person.column_methods_hash.include?(:last_name)
691
+
692
+ ActiveRecord::Migration.remove_column :people, :last_name
693
+
694
+ Person.reset_column_information
695
+ assert !Person.column_methods_hash.include?(:last_name)
696
+ end
697
+
698
+ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
699
+ def testing_table_for_positioning
700
+ Person.connection.create_table :testings, :id => false do |t|
701
+ t.column :first, :integer
702
+ t.column :second, :integer
703
+ t.column :third, :integer
704
+ end
705
+
706
+ yield Person.connection
707
+ ensure
708
+ Person.connection.drop_table :testings rescue nil
709
+ end
710
+ protected :testing_table_for_positioning
711
+
712
+ def test_column_positioning
713
+ testing_table_for_positioning do |conn|
714
+ assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name }
715
+ end
716
+ end
717
+
718
+ def test_add_column_with_positioning
719
+ testing_table_for_positioning do |conn|
720
+ conn.add_column :testings, :new_col, :integer
721
+ assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name }
722
+ end
723
+ testing_table_for_positioning do |conn|
724
+ conn.add_column :testings, :new_col, :integer, :first => true
725
+ assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name }
726
+ end
727
+ testing_table_for_positioning do |conn|
728
+ conn.add_column :testings, :new_col, :integer, :after => :first
729
+ assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name }
730
+ end
731
+ end
732
+
733
+ def test_change_column_with_positioning
734
+ testing_table_for_positioning do |conn|
735
+ conn.change_column :testings, :second, :integer, :first => true
736
+ assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name }
737
+ end
738
+ testing_table_for_positioning do |conn|
739
+ conn.change_column :testings, :second, :integer, :after => :third
740
+ assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name }
741
+ end
742
+ end
743
+ end
744
+
745
+ if (!current_adapter?(:IBM_DBAdapter) || @ibm_db_rename_supported)
746
+ def test_add_rename
747
+ Person.delete_all
748
+
749
+ begin
750
+ Person.connection.add_column "people", "girlfriend", :string
751
+ Person.reset_column_information
752
+ Person.create :girlfriend => 'bobette'
753
+
754
+ Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
755
+
756
+ Person.reset_column_information
757
+ bob = Person.find(:first)
758
+
759
+ assert_equal "bobette", bob.exgirlfriend
760
+ ensure
761
+ Person.connection.remove_column("people", "girlfriend") rescue nil
762
+ Person.connection.remove_column("people", "exgirlfriend") rescue nil
763
+ end
764
+
765
+ end
766
+
767
+ def test_rename_column_using_symbol_arguments
768
+ begin
769
+ names_before = Person.find(:all).map(&:first_name)
770
+ Person.connection.rename_column :people, :first_name, :nick_name
771
+ Person.reset_column_information
772
+ assert Person.column_names.include?("nick_name")
773
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
774
+ ensure
775
+ Person.connection.remove_column("people","nick_name")
776
+ Person.connection.add_column("people","first_name", :string)
777
+ end
778
+ end
779
+
780
+ def test_rename_column
781
+ begin
782
+ names_before = Person.find(:all).map(&:first_name)
783
+ Person.connection.rename_column "people", "first_name", "nick_name"
784
+ Person.reset_column_information
785
+ assert Person.column_names.include?("nick_name")
786
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
787
+ ensure
788
+ Person.connection.remove_column("people","nick_name")
789
+ Person.connection.add_column("people","first_name", :string)
790
+ end
791
+ end
792
+
793
+ def test_rename_column_preserves_default_value_not_null
794
+ begin
795
+ default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default
796
+ assert_equal 70000, default_before
797
+ Developer.connection.rename_column "developers", "salary", "anual_salary"
798
+ Developer.reset_column_information
799
+ assert Developer.column_names.include?("anual_salary")
800
+ default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default
801
+ assert_equal 70000, default_after
802
+ ensure
803
+ Developer.connection.rename_column "developers", "anual_salary", "salary"
804
+ Developer.reset_column_information
805
+ end
806
+ end
807
+
808
+ def test_rename_nonexistent_column
809
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
810
+ table.column :hat_name, :string, :default => nil
811
+ end
812
+ exception = if current_adapter?(:PostgreSQLAdapter)
813
+ ActiveRecord::StatementInvalid
814
+ else
815
+ ActiveRecord::ActiveRecordError
816
+ end
817
+ assert_raise(exception) do
818
+ Person.connection.rename_column "hats", "nonexistent", "should_fail"
819
+ end
820
+ ensure
821
+ ActiveRecord::Base.connection.drop_table(:hats)
822
+ end
823
+
824
+ def test_rename_column_with_sql_reserved_word
825
+ begin
826
+ assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
827
+ Person.reset_column_information
828
+ assert Person.column_names.include?("group")
829
+ ensure
830
+ Person.connection.remove_column("people", "group") rescue nil
831
+ Person.connection.add_column("people", "first_name", :string) rescue nil
832
+ end
833
+ end
834
+ end
835
+
836
+ unless (current_adapter?(:IBM_DBAdapter)) #Cannot alter a object when there is another object depending on it
837
+ def test_rename_column_with_an_index
838
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
839
+ table.column :hat_name, :string, :limit => 100
840
+ table.column :hat_size, :integer
841
+ end
842
+ Person.connection.add_index :hats, :hat_name
843
+ assert_nothing_raised do
844
+ Person.connection.rename_column "hats", "hat_name", "name"
845
+ end
846
+ ensure
847
+ ActiveRecord::Base.connection.drop_table(:hats)
848
+ end
849
+ end
850
+
851
+ def test_rename_column_with_an_index
852
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
853
+ table.column :hat_name, :string, :limit => 100
854
+ table.column :hat_size, :integer
855
+ end
856
+ Person.connection.add_index :hats, :hat_name
857
+ assert_nothing_raised do
858
+ Person.connection.rename_column "hats", "hat_name", "name"
859
+ end
860
+ ensure
861
+ ActiveRecord::Base.connection.drop_table(:hats)
862
+ end
863
+
864
+ def test_remove_column_with_index
865
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
866
+ table.column :hat_name, :string, :limit => 100
867
+ table.column :hat_size, :integer
868
+ end
869
+ ActiveRecord::Base.connection.add_index "hats", "hat_size"
870
+
871
+ assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
872
+ ensure
873
+ ActiveRecord::Base.connection.drop_table(:hats)
874
+ end
875
+
876
+ unless (current_adapter?(:IBM_DBAdapter)) #Cannot alter a object when there is another object depending on it
877
+ def test_remove_column_with_multi_column_index
878
+ ActiveRecord::Base.connection.create_table(:hats) do |table|
879
+ table.column :hat_name, :string, :limit => 100
880
+ table.column :hat_size, :integer
881
+ table.column :hat_style, :string, :limit => 100
882
+ end
883
+ ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true
884
+
885
+ assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
886
+ ensure
887
+ ActiveRecord::Base.connection.drop_table(:hats)
888
+ end
889
+ end
890
+
891
+ unless current_adapter?(:IBM_DBAdapter) #incompatible types changes
892
+ def test_change_type_of_not_null_column
893
+ assert_nothing_raised do
894
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
895
+ Topic.reset_column_information
896
+
897
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
898
+ Topic.reset_column_information
899
+ end
900
+ end
901
+ end
902
+
903
+ if current_adapter?(:SQLiteAdapter)
904
+ def test_rename_table_for_sqlite_should_work_with_reserved_words
905
+ begin
906
+ assert_nothing_raised do
907
+ ActiveRecord::Base.connection.rename_table :references, :old_references
908
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
909
+ t.column :url, :string
910
+ end
911
+ end
912
+
913
+ assert_nothing_raised { ActiveRecord::Base.connection.rename_table :octopuses, :references }
914
+
915
+ # Using explicit id in insert for compatibility across all databases
916
+ con = ActiveRecord::Base.connection
917
+ assert_nothing_raised do
918
+ con.execute "INSERT INTO 'references' (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://rubyonrails.com')"
919
+ end
920
+ assert_equal 'http://rubyonrails.com', ActiveRecord::Base.connection.select_value("SELECT url FROM 'references' WHERE id=1")
921
+
922
+ ensure
923
+ ActiveRecord::Base.connection.drop_table :references
924
+ ActiveRecord::Base.connection.rename_table :old_references, :references
925
+ end
926
+ end
927
+ end
928
+
929
+ def test_rename_table
930
+ begin
931
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
932
+ t.column :url, :string
933
+ end
934
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
935
+
936
+ # Using explicit id in insert for compatibility across all databases
937
+ con = ActiveRecord::Base.connection
938
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
939
+ 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')" }
940
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
941
+
942
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
943
+
944
+ ensure
945
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
946
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
947
+ end
948
+ end
949
+
950
+ def test_change_column_nullability
951
+ Person.delete_all
952
+ Person.connection.add_column "people", "funny", :boolean
953
+ Person.reset_column_information
954
+ assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
955
+ unless current_adapter?(:IBM_DBAdapter) # incompatible types changes
956
+ Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
957
+ Person.reset_column_information
958
+ assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
959
+ Person.connection.change_column "people", "funny", :boolean, :null => true
960
+ Person.reset_column_information
961
+ assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
962
+ end
963
+ end
964
+
965
+ def test_rename_table_with_an_index
966
+ begin
967
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
968
+ t.column :url, :string
969
+ end
970
+ ActiveRecord::Base.connection.add_index :octopuses, :url
971
+
972
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
973
+
974
+ # Using explicit id in insert for compatibility across all databases
975
+ con = ActiveRecord::Base.connection
976
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
977
+ 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')" }
978
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
979
+
980
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
981
+ assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
982
+ ensure
983
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
984
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
985
+ end
986
+ end
987
+
988
+ unless current_adapter?(:IBM_DBAdapter) # incompatible types changes
989
+ def test_change_column
990
+ Person.connection.add_column 'people', 'age', :integer
991
+ label = "test_change_column Columns"
992
+ old_columns = Person.connection.columns(Person.table_name, label)
993
+ assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
994
+
995
+ assert_nothing_raised { Person.connection.change_column "people", "age", :string }
996
+
997
+ new_columns = Person.connection.columns(Person.table_name, label)
998
+ assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
999
+ assert new_columns.find { |c| c.name == 'age' and c.type == :string }
1000
+
1001
+ old_columns = Topic.connection.columns(Topic.table_name, label)
1002
+ assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
1003
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
1004
+ new_columns = Topic.connection.columns(Topic.table_name, label)
1005
+ assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
1006
+ assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
1007
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
1008
+ end
1009
+ end
1010
+
1011
+ def test_change_column_with_nil_default
1012
+ Person.connection.add_column "people", "contributor", :boolean, :default => true
1013
+ Person.reset_column_information
1014
+ assert Person.new.contributor?
1015
+ unless current_adapter?(:IBM_DBAdapter) # incompatible types changes
1016
+ assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
1017
+ Person.reset_column_information
1018
+ assert !Person.new.contributor?
1019
+ assert_nil Person.new.contributor
1020
+ end
1021
+ ensure
1022
+ Person.connection.remove_column("people", "contributor") rescue nil
1023
+ end
1024
+
1025
+ def test_change_column_with_new_default
1026
+ Person.connection.add_column "people", "administrator", :boolean, :default => true
1027
+ Person.reset_column_information
1028
+ assert Person.new.administrator?
1029
+ unless current_adapter?(:IBM_DBAdapter) # incompatible types changes
1030
+ assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
1031
+ Person.reset_column_information
1032
+ assert !Person.new.administrator?
1033
+ end
1034
+ ensure
1035
+ Person.connection.remove_column("people", "administrator") rescue nil
1036
+ end
1037
+
1038
+ def test_change_column_default
1039
+ Person.connection.change_column_default "people", "first_name", "Tester"
1040
+ Person.reset_column_information
1041
+ assert_equal "Tester", Person.new.first_name
1042
+ end
1043
+
1044
+ def test_change_column_quotes_column_names
1045
+ Person.connection.create_table :testings do |t|
1046
+ if current_adapter?(:IBM_DBAdapter)
1047
+ t.column :select, :string, :limit => 5
1048
+ else
1049
+ t.column :select, :string
1050
+ end
1051
+ end
1052
+
1053
+ assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }
1054
+
1055
+ # Oracle needs primary key value from sequence
1056
+ if current_adapter?(:OracleAdapter)
1057
+ assert_nothing_raised { Person.connection.execute "insert into testings (id, #{Person.connection.quote_column_name('select')}) values (testings_seq.nextval, '7 chars')" }
1058
+ else
1059
+ assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
1060
+ end
1061
+ ensure
1062
+ Person.connection.drop_table :testings rescue nil
1063
+ end
1064
+
1065
+ def test_keeping_default_and_notnull_constaint_on_change
1066
+ Person.connection.create_table :testings do |t|
1067
+ t.column :title, :string
1068
+ end
1069
+ person_klass = Class.new(Person)
1070
+ person_klass.set_table_name 'testings'
1071
+
1072
+ person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
1073
+ person_klass.reset_column_information
1074
+ assert_equal 99, person_klass.columns_hash["wealth"].default
1075
+ assert_equal false, person_klass.columns_hash["wealth"].null
1076
+ # Oracle needs primary key value from sequence
1077
+ if current_adapter?(:OracleAdapter)
1078
+ assert_nothing_raised {person_klass.connection.execute("insert into testings (id, title) values (testings_seq.nextval, 'tester')")}
1079
+ else
1080
+ assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
1081
+ end
1082
+
1083
+ # change column default to see that column doesn't lose its not null definition
1084
+ person_klass.connection.change_column_default "testings", "wealth", 100
1085
+ person_klass.reset_column_information
1086
+ assert_equal 100, person_klass.columns_hash["wealth"].default
1087
+ assert_equal false, person_klass.columns_hash["wealth"].null
1088
+
1089
+ # rename column to see that column doesn't lose its not null and/or default definition
1090
+ if (!current_adapter?(:IBM_DBAdapter) || @ibm_db_rename_supported)
1091
+ person_klass.connection.rename_column "testings", "wealth", "money"
1092
+ person_klass.reset_column_information
1093
+ assert_nil person_klass.columns_hash["wealth"]
1094
+ assert_equal 100, person_klass.columns_hash["money"].default
1095
+ assert_equal false, person_klass.columns_hash["money"].null
1096
+ end
1097
+
1098
+ # change column
1099
+ unless (current_adapter?(:IBM_DBAdapter) && !@ibm_db_rename_supported)
1100
+ person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
1101
+ person_klass.reset_column_information
1102
+ assert_equal 1000, person_klass.columns_hash["money"].default
1103
+ assert_equal false, person_klass.columns_hash["money"].null
1104
+ else
1105
+ person_klass.connection.change_column "testings", "wealth", :decimal, :precision => 15, :scale => 1,:null => false, :default => 1000
1106
+ person_klass.reset_column_information
1107
+ assert_equal 1000, person_klass.columns_hash["wealth"].default
1108
+ assert_equal false, person_klass.columns_hash["wealth"].null
1109
+ end
1110
+
1111
+ # change column, make it nullable and clear default
1112
+ unless (current_adapter?(:IBM_DBAdapter) && !@ibm_db_rename_supported)
1113
+ person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
1114
+ person_klass.reset_column_information
1115
+ assert_nil person_klass.columns_hash["money"].default
1116
+ assert_equal true, person_klass.columns_hash["money"].null
1117
+ else
1118
+ person_klass.connection.change_column "testings", "wealth", :decimal, :precision => 20, :scale => 2, :null => true, :default => nil
1119
+ person_klass.reset_column_information
1120
+ assert_nil person_klass.columns_hash["wealth"].default
1121
+ assert_equal true, person_klass.columns_hash["wealth"].null
1122
+ end
1123
+
1124
+ # change_column_null, make it not nullable and set null values to a default value
1125
+ unless (current_adapter?(:IBM_DBAdapter) && !@ibm_db_rename_supported)
1126
+ person_klass.connection.execute('UPDATE testings SET money = NULL')
1127
+ person_klass.connection.change_column_null "testings", "money", false, 2000
1128
+ person_klass.reset_column_information
1129
+ assert_nil person_klass.columns_hash["money"].default
1130
+ assert_equal false, person_klass.columns_hash["money"].null
1131
+ assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
1132
+ else
1133
+ # Trying to set the value of the column wealth to NULL and
1134
+ # in the next statement a not null constraint is being applied which is wrong.
1135
+ #person_klass.connection.execute('UPDATE testings SET wealth = NULL')
1136
+ person_klass.connection.change_column_null "testings", "wealth", false, 2000
1137
+ person_klass.reset_column_information
1138
+ assert_equal false, person_klass.columns_hash["wealth"].null
1139
+ assert_equal 2000, person_klass.columns_hash["wealth"].default
1140
+ end
1141
+ ensure
1142
+ Person.connection.drop_table :testings rescue nil
1143
+ end
1144
+
1145
+ def test_change_column_default_to_null
1146
+ Person.connection.change_column_default "people", "first_name", nil
1147
+ Person.reset_column_information
1148
+ assert_nil Person.new.first_name
1149
+ end
1150
+
1151
+ def test_column_exists
1152
+ Person.connection.create_table :testings do |t|
1153
+ t.column :foo, :string
1154
+ end
1155
+
1156
+ assert Person.connection.column_exists?(:testings, :foo)
1157
+ assert !Person.connection.column_exists?(:testings, :bar)
1158
+ ensure
1159
+ Person.connection.drop_table :testings rescue nil
1160
+ end
1161
+
1162
+ def test_column_exists_with_type
1163
+ Person.connection.create_table :testings do |t|
1164
+ t.column :foo, :string
1165
+ t.column :bar, :decimal, :precision => 8, :scale => 2
1166
+ end
1167
+
1168
+ assert Person.connection.column_exists?(:testings, :foo, :string)
1169
+ assert !Person.connection.column_exists?(:testings, :foo, :integer)
1170
+ assert Person.connection.column_exists?(:testings, :bar, :decimal)
1171
+ assert !Person.connection.column_exists?(:testings, :bar, :integer)
1172
+ ensure
1173
+ Person.connection.drop_table :testings rescue nil
1174
+ end
1175
+
1176
+ def test_column_exists_with_definition
1177
+ Person.connection.create_table :testings do |t|
1178
+ t.column :foo, :string, :limit => 100
1179
+ t.column :bar, :decimal, :precision => 8, :scale => 2
1180
+ end
1181
+
1182
+ assert Person.connection.column_exists?(:testings, :foo, :string, :limit => 100)
1183
+ assert !Person.connection.column_exists?(:testings, :foo, :string, :limit => 50)
1184
+ assert Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 8, :scale => 2)
1185
+ assert !Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 10, :scale => 2)
1186
+ ensure
1187
+ Person.connection.drop_table :testings rescue nil
1188
+ end
1189
+
1190
+ def test_add_table
1191
+ assert !Reminder.table_exists?
1192
+
1193
+ WeNeedReminders.up
1194
+
1195
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
1196
+ assert_equal "hello world", Reminder.find(:first).content
1197
+
1198
+ WeNeedReminders.down
1199
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
1200
+ end
1201
+
1202
+ def test_add_table_with_decimals
1203
+ Person.connection.drop_table :big_numbers rescue nil
1204
+
1205
+ assert !BigNumber.table_exists?
1206
+ GiveMeBigNumbers.up
1207
+
1208
+ assert BigNumber.create(
1209
+ :bank_balance => 1586.43,
1210
+ :big_bank_balance => BigDecimal("1000234000567.95"),
1211
+ :world_population => 6000000000,
1212
+ :my_house_population => 3,
1213
+ :value_of_e => BigDecimal("2.7182818284590452353602875")
1214
+ )
1215
+
1216
+ b = BigNumber.find(:first)
1217
+ assert_not_nil b
1218
+
1219
+ assert_not_nil b.bank_balance
1220
+ assert_not_nil b.big_bank_balance
1221
+ assert_not_nil b.world_population
1222
+ assert_not_nil b.my_house_population
1223
+ assert_not_nil b.value_of_e
1224
+
1225
+ # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
1226
+ # is_a?(Bignum)
1227
+ unless current_adapter?(:IBM_DBAdapter) # incompatible types retrieved
1228
+ assert_kind_of Integer, b.world_population
1229
+ else
1230
+ assert_kind_of BigDecimal, b.world_population
1231
+ end
1232
+ assert_equal 6000000000, b.world_population
1233
+ unless current_adapter?(:IBM_DBAdapter) # incompatible types retrieved
1234
+ assert_kind_of Fixnum, b.my_house_population
1235
+ else
1236
+ assert_kind_of BigDecimal, b.my_house_population
1237
+ end
1238
+ assert_equal 3, b.my_house_population
1239
+ assert_kind_of BigDecimal, b.bank_balance
1240
+ assert_equal BigDecimal("1586.43"), b.bank_balance
1241
+ assert_kind_of BigDecimal, b.big_bank_balance
1242
+ assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
1243
+
1244
+ # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
1245
+ # precision/scale explicitly left out. By the SQL standard, numbers
1246
+ # assigned to this field should be truncated but that's seldom respected.
1247
+ if current_adapter?(:PostgreSQLAdapter)
1248
+ # - PostgreSQL changes the SQL spec on columns declared simply as
1249
+ # "decimal" to something more useful: instead of being given a scale
1250
+ # of 0, they take on the compile-time limit for precision and scale,
1251
+ # so the following should succeed unless you have used really wacky
1252
+ # compilation options
1253
+ # - SQLite2 has the default behavior of preserving all data sent in,
1254
+ # so this happens there too
1255
+ assert_kind_of BigDecimal, b.value_of_e
1256
+ assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
1257
+ elsif current_adapter?(:SQLiteAdapter)
1258
+ # - SQLite3 stores a float, in violation of SQL
1259
+ assert_kind_of BigDecimal, b.value_of_e
1260
+ assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
1261
+ else
1262
+ # - SQL standard is an integer
1263
+ unless current_adapter?(:IBM_DBAdapter) # incompatible types retrieved
1264
+ assert_kind_of Fixnum, b.value_of_e
1265
+ else
1266
+ assert_kind_of BigDecimal, b.value_of_e
1267
+ end
1268
+ assert_equal 2, b.value_of_e
1269
+ end
1270
+
1271
+ GiveMeBigNumbers.down
1272
+ assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
1273
+ end
1274
+
1275
+ def test_migrator
1276
+ assert !Person.column_methods_hash.include?(:last_name)
1277
+ assert !Reminder.table_exists?
1278
+
1279
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
1280
+
1281
+ assert_equal 3, ActiveRecord::Migrator.current_version
1282
+ Person.reset_column_information
1283
+ assert Person.column_methods_hash.include?(:last_name)
1284
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
1285
+ assert_equal "hello world", Reminder.find(:first).content
1286
+
1287
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
1288
+
1289
+ assert_equal 0, ActiveRecord::Migrator.current_version
1290
+ Person.reset_column_information
1291
+ assert !Person.column_methods_hash.include?(:last_name)
1292
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
1293
+ end
1294
+
1295
+ def test_migrator_one_up
1296
+ assert !Person.column_methods_hash.include?(:last_name)
1297
+ assert !Reminder.table_exists?
1298
+
1299
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1300
+
1301
+ Person.reset_column_information
1302
+ assert Person.column_methods_hash.include?(:last_name)
1303
+ assert !Reminder.table_exists?
1304
+
1305
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
1306
+
1307
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
1308
+ assert_equal "hello world", Reminder.find(:first).content
1309
+ end
1310
+
1311
+ def test_migrator_one_down
1312
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
1313
+
1314
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
1315
+
1316
+ Person.reset_column_information
1317
+ assert Person.column_methods_hash.include?(:last_name)
1318
+ assert !Reminder.table_exists?
1319
+ end
1320
+
1321
+ def test_migrator_one_up_one_down
1322
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1323
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
1324
+
1325
+ assert !Person.column_methods_hash.include?(:last_name)
1326
+ assert !Reminder.table_exists?
1327
+ end
1328
+
1329
+ def test_migrator_double_up
1330
+ assert_equal(0, ActiveRecord::Migrator.current_version)
1331
+ ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
1332
+ assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) }
1333
+ assert_equal(1, ActiveRecord::Migrator.current_version)
1334
+ end
1335
+
1336
+ def test_migrator_double_down
1337
+ assert_equal(0, ActiveRecord::Migrator.current_version)
1338
+ ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
1339
+ ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1)
1340
+ assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) }
1341
+ assert_equal(0, ActiveRecord::Migrator.current_version)
1342
+ end
1343
+
1344
+ if ActiveRecord::Base.connection.supports_ddl_transactions?
1345
+ def test_migrator_one_up_with_exception_and_rollback
1346
+ assert !Person.column_methods_hash.include?(:last_name)
1347
+
1348
+ e = assert_raise(StandardError) do
1349
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
1350
+ end
1351
+
1352
+ assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
1353
+
1354
+ Person.reset_column_information
1355
+ assert !Person.column_methods_hash.include?(:last_name)
1356
+ end
1357
+ end
1358
+
1359
+ def test_finds_migrations
1360
+ migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
1361
+
1362
+ [[1, 'PeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
1363
+ assert_equal migrations[i].version, pair.first
1364
+ assert_equal migrations[i].name, pair.last
1365
+ end
1366
+ end
1367
+
1368
+ def test_finds_pending_migrations
1369
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1)
1370
+ migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2").pending_migrations
1371
+
1372
+ assert_equal 1, migrations.size
1373
+ assert_equal migrations[0].version, 3
1374
+ assert_equal migrations[0].name, 'InnocentJointable'
1375
+ end
1376
+
1377
+ def test_relative_migrations
1378
+ $".delete_if do |fname|
1379
+ fname == (MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
1380
+ end
1381
+ Object.send(:remove_const, :PeopleHaveLastNames)
1382
+
1383
+ Dir.chdir(MIGRATIONS_ROOT) do
1384
+ ActiveRecord::Migrator.up("valid/", 1)
1385
+ end
1386
+
1387
+ assert defined?(PeopleHaveLastNames)
1388
+ end
1389
+
1390
+ def test_only_loads_pending_migrations
1391
+ # migrate up to 1
1392
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1393
+
1394
+ # now unload the migrations that have been defined
1395
+ Object.send(:remove_const, :PeopleHaveLastNames)
1396
+
1397
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
1398
+
1399
+ assert !defined? PeopleHaveLastNames
1400
+
1401
+ %w(WeNeedReminders, InnocentJointable).each do |migration|
1402
+ assert defined? migration
1403
+ end
1404
+
1405
+ ensure
1406
+ load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
1407
+ end
1408
+
1409
+ def test_target_version_zero_should_run_only_once
1410
+ # migrate up to 1
1411
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
1412
+
1413
+ # migrate down to 0
1414
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
1415
+
1416
+ # now unload the migrations that have been defined
1417
+ PeopleHaveLastNames.unloadable
1418
+ ActiveSupport::Dependencies.remove_unloadable_constants!
1419
+
1420
+ # migrate down to 0 again
1421
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
1422
+
1423
+ assert !defined? PeopleHaveLastNames
1424
+ ensure
1425
+ load(MIGRATIONS_ROOT + "/valid/1_people_have_last_names.rb")
1426
+ end
1427
+
1428
+ def test_migrator_db_has_no_schema_migrations_table
1429
+ # Oracle adapter raises error if semicolon is present as last character
1430
+ if current_adapter?(:OracleAdapter)
1431
+ ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations")
1432
+ else
1433
+ ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations;")
1434
+ end
1435
+ assert_nothing_raised do
1436
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
1437
+ end
1438
+ end
1439
+
1440
+ def test_migrator_verbosity
1441
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1442
+ assert PeopleHaveLastNames.message_count > 0
1443
+ PeopleHaveLastNames.message_count = 0
1444
+
1445
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
1446
+ assert PeopleHaveLastNames.message_count > 0
1447
+ PeopleHaveLastNames.message_count = 0
1448
+ end
1449
+
1450
+ def test_migrator_verbosity_off
1451
+ PeopleHaveLastNames.verbose = false
1452
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1453
+ assert PeopleHaveLastNames.message_count.zero?
1454
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
1455
+ assert PeopleHaveLastNames.message_count.zero?
1456
+ end
1457
+
1458
+ def test_migrator_going_down_due_to_version_target
1459
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
1460
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
1461
+
1462
+ assert !Person.column_methods_hash.include?(:last_name)
1463
+ assert !Reminder.table_exists?
1464
+
1465
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1466
+
1467
+ Person.reset_column_information
1468
+ assert Person.column_methods_hash.include?(:last_name)
1469
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
1470
+ assert_equal "hello world", Reminder.find(:first).content
1471
+ end
1472
+
1473
+ def test_migrator_rollback
1474
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1475
+ assert_equal(3, ActiveRecord::Migrator.current_version)
1476
+
1477
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1478
+ assert_equal(2, ActiveRecord::Migrator.current_version)
1479
+
1480
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1481
+ assert_equal(1, ActiveRecord::Migrator.current_version)
1482
+
1483
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1484
+ assert_equal(0, ActiveRecord::Migrator.current_version)
1485
+
1486
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1487
+ assert_equal(0, ActiveRecord::Migrator.current_version)
1488
+ end
1489
+
1490
+ def test_migrator_forward
1491
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
1492
+ assert_equal(1, ActiveRecord::Migrator.current_version)
1493
+
1494
+ ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid", 2)
1495
+ assert_equal(3, ActiveRecord::Migrator.current_version)
1496
+
1497
+ ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid")
1498
+ assert_equal(3, ActiveRecord::Migrator.current_version)
1499
+ end
1500
+
1501
+ def test_get_all_versions
1502
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1503
+ assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
1504
+
1505
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1506
+ assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
1507
+
1508
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1509
+ assert_equal([1], ActiveRecord::Migrator.get_all_versions)
1510
+
1511
+ ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
1512
+ assert_equal([], ActiveRecord::Migrator.get_all_versions)
1513
+ end
1514
+
1515
+ def test_schema_migrations_table_name
1516
+ ActiveRecord::Base.table_name_prefix = "prefix_"
1517
+ ActiveRecord::Base.table_name_suffix = "_suffix"
1518
+ Reminder.reset_table_name
1519
+ assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
1520
+ ActiveRecord::Base.table_name_prefix = ""
1521
+ ActiveRecord::Base.table_name_suffix = ""
1522
+ Reminder.reset_table_name
1523
+ assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
1524
+ ensure
1525
+ ActiveRecord::Base.table_name_prefix = ""
1526
+ ActiveRecord::Base.table_name_suffix = ""
1527
+ end
1528
+
1529
+ def test_proper_table_name
1530
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
1531
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
1532
+ assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
1533
+ Reminder.reset_table_name
1534
+ assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
1535
+
1536
+ # Use the model's own prefix/suffix if a model is given
1537
+ ActiveRecord::Base.table_name_prefix = "ARprefix_"
1538
+ ActiveRecord::Base.table_name_suffix = "_ARsuffix"
1539
+ Reminder.table_name_prefix = 'prefix_'
1540
+ Reminder.table_name_suffix = '_suffix'
1541
+ Reminder.reset_table_name
1542
+ assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
1543
+ Reminder.table_name_prefix = ''
1544
+ Reminder.table_name_suffix = ''
1545
+ Reminder.reset_table_name
1546
+
1547
+ # Use AR::Base's prefix/suffix if string or symbol is given
1548
+ ActiveRecord::Base.table_name_prefix = "prefix_"
1549
+ ActiveRecord::Base.table_name_suffix = "_suffix"
1550
+ Reminder.reset_table_name
1551
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
1552
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
1553
+ ActiveRecord::Base.table_name_prefix = ""
1554
+ ActiveRecord::Base.table_name_suffix = ""
1555
+ Reminder.reset_table_name
1556
+ end
1557
+
1558
+ def test_add_drop_table_with_prefix_and_suffix
1559
+ assert !Reminder.table_exists?
1560
+ ActiveRecord::Base.table_name_prefix = 'prefix_'
1561
+ ActiveRecord::Base.table_name_suffix = '_suffix'
1562
+ Reminder.reset_table_name
1563
+ Reminder.reset_sequence_name
1564
+ WeNeedReminders.up
1565
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
1566
+ assert_equal "hello world", Reminder.find(:first).content
1567
+
1568
+ WeNeedReminders.down
1569
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
1570
+ ensure
1571
+ ActiveRecord::Base.table_name_prefix = ''
1572
+ ActiveRecord::Base.table_name_suffix = ''
1573
+ Reminder.reset_table_name
1574
+ Reminder.reset_sequence_name
1575
+ end
1576
+
1577
+ def test_create_table_with_binary_column
1578
+ Person.connection.drop_table :binary_testings rescue nil
1579
+
1580
+ assert_nothing_raised {
1581
+ Person.connection.create_table :binary_testings do |t|
1582
+ t.column "data", :binary, :null => false
1583
+ end
1584
+ }
1585
+
1586
+ columns = Person.connection.columns(:binary_testings)
1587
+ data_column = columns.detect { |c| c.name == "data" }
1588
+
1589
+ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
1590
+ assert_equal '', data_column.default
1591
+ else
1592
+ assert_nil data_column.default
1593
+ end
1594
+
1595
+ Person.connection.drop_table :binary_testings rescue nil
1596
+ end
1597
+
1598
+ def test_migrator_with_duplicates
1599
+ assert_raise(ActiveRecord::DuplicateMigrationVersionError) do
1600
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
1601
+ end
1602
+ end
1603
+
1604
+ def test_migrator_with_duplicate_names
1605
+ assert_raise(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
1606
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
1607
+ end
1608
+ end
1609
+
1610
+ def test_migrator_with_missing_version_numbers
1611
+ assert_raise(ActiveRecord::UnknownMigrationVersionError) do
1612
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
1613
+ end
1614
+ end
1615
+
1616
+ def test_create_table_with_custom_sequence_name
1617
+ return unless current_adapter? :OracleAdapter
1618
+
1619
+ # table name is 29 chars, the standard sequence name will
1620
+ # be 33 chars and should be shortened
1621
+ assert_nothing_raised do
1622
+ begin
1623
+ Person.connection.create_table :table_with_name_thats_just_ok do |t|
1624
+ t.column :foo, :string, :null => false
1625
+ end
1626
+ ensure
1627
+ Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
1628
+ end
1629
+ end
1630
+
1631
+ # should be all good w/ a custom sequence name
1632
+ assert_nothing_raised do
1633
+ begin
1634
+ Person.connection.create_table :table_with_name_thats_just_ok,
1635
+ :sequence_name => 'suitably_short_seq' do |t|
1636
+ t.column :foo, :string, :null => false
1637
+ end
1638
+
1639
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
1640
+
1641
+ ensure
1642
+ Person.connection.drop_table :table_with_name_thats_just_ok,
1643
+ :sequence_name => 'suitably_short_seq' rescue nil
1644
+ end
1645
+ end
1646
+
1647
+ # confirm the custom sequence got dropped
1648
+ assert_raise(ActiveRecord::StatementInvalid) do
1649
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
1650
+ end
1651
+ end
1652
+
1653
+ protected
1654
+ def with_env_tz(new_tz = 'US/Eastern')
1655
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
1656
+ yield
1657
+ ensure
1658
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
1659
+ end
1660
+
1661
+ end
1662
+
1663
+ class SexyMigrationsTest < ActiveRecord::TestCase
1664
+ def test_references_column_type_adds_id
1665
+ with_new_table do |t|
1666
+ t.expects(:column).with('customer_id', :integer, {})
1667
+ t.references :customer
1668
+ end
1669
+ end
1670
+
1671
+ def test_references_column_type_with_polymorphic_adds_type
1672
+ with_new_table do |t|
1673
+ t.expects(:column).with('taggable_type', :string, {})
1674
+ t.expects(:column).with('taggable_id', :integer, {})
1675
+ t.references :taggable, :polymorphic => true
1676
+ end
1677
+ end
1678
+
1679
+ def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
1680
+ with_new_table do |t|
1681
+ t.expects(:column).with('taggable_type', :string, {:null => false})
1682
+ t.expects(:column).with('taggable_id', :integer, {:null => false})
1683
+ t.references :taggable, :polymorphic => true, :null => false
1684
+ end
1685
+ end
1686
+
1687
+ def test_belongs_to_works_like_references
1688
+ with_new_table do |t|
1689
+ t.expects(:column).with('customer_id', :integer, {})
1690
+ t.belongs_to :customer
1691
+ end
1692
+ end
1693
+
1694
+ def test_timestamps_creates_updated_at_and_created_at
1695
+ with_new_table do |t|
1696
+ t.expects(:column).with(:created_at, :datetime, kind_of(Hash))
1697
+ t.expects(:column).with(:updated_at, :datetime, kind_of(Hash))
1698
+ t.timestamps
1699
+ end
1700
+ end
1701
+
1702
+ def test_integer_creates_integer_column
1703
+ with_new_table do |t|
1704
+ t.expects(:column).with(:foo, 'integer', {})
1705
+ t.expects(:column).with(:bar, 'integer', {})
1706
+ t.integer :foo, :bar
1707
+ end
1708
+ end
1709
+
1710
+ def test_string_creates_string_column
1711
+ with_new_table do |t|
1712
+ t.expects(:column).with(:foo, 'string', {})
1713
+ t.expects(:column).with(:bar, 'string', {})
1714
+ t.string :foo, :bar
1715
+ end
1716
+ end
1717
+
1718
+ if current_adapter?(:PostgreSQLAdapter)
1719
+ def test_xml_creates_xml_column
1720
+ with_new_table do |t|
1721
+ t.expects(:column).with(:data, 'xml', {})
1722
+ t.xml :data
1723
+ end
1724
+ end
1725
+ end
1726
+
1727
+ protected
1728
+ def with_new_table
1729
+ Person.connection.create_table :delete_me, :force => true do |t|
1730
+ yield t
1731
+ end
1732
+ ensure
1733
+ Person.connection.drop_table :delete_me rescue nil
1734
+ end
1735
+
1736
+ end # SexyMigrationsTest
1737
+
1738
+ class MigrationLoggerTest < ActiveRecord::TestCase
1739
+ def setup
1740
+ Object.send(:remove_const, :InnocentJointable)
1741
+ end
1742
+
1743
+ def test_migration_should_be_run_without_logger
1744
+ previous_logger = ActiveRecord::Base.logger
1745
+ ActiveRecord::Base.logger = nil
1746
+ assert_nothing_raised do
1747
+ ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
1748
+ end
1749
+ ensure
1750
+ ActiveRecord::Base.logger = previous_logger
1751
+ end
1752
+ end
1753
+
1754
+ class InterleavedMigrationsTest < ActiveRecord::TestCase
1755
+ def setup
1756
+ Object.send(:remove_const, :PeopleHaveLastNames)
1757
+ end
1758
+
1759
+ def test_migrator_interleaved_migrations
1760
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
1761
+
1762
+ assert_nothing_raised do
1763
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
1764
+ end
1765
+
1766
+ Person.reset_column_information
1767
+ assert Person.column_methods_hash.include?(:last_name)
1768
+
1769
+ Object.send(:remove_const, :PeopleHaveLastNames)
1770
+ Object.send(:remove_const, :InnocentJointable)
1771
+ assert_nothing_raised do
1772
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/interleaved/pass_3")
1773
+ end
1774
+ end
1775
+ end
1776
+
1777
+ class ReservedWordsMigrationTest < ActiveRecord::TestCase
1778
+ def test_drop_index_from_table_named_values
1779
+ connection = Person.connection
1780
+ connection.create_table :values, :force => true do |t|
1781
+ t.integer :value
1782
+ end
1783
+
1784
+ assert_nothing_raised do
1785
+ connection.add_index :values, :value
1786
+ connection.remove_index :values, :column => :value
1787
+ end
1788
+
1789
+ connection.drop_table :values rescue nil
1790
+ end
1791
+ end
1792
+
1793
+
1794
+ class ChangeTableMigrationsTest < ActiveRecord::TestCase
1795
+ def setup
1796
+ @connection = Person.connection
1797
+ @connection.create_table :delete_me, :force => true do |t|
1798
+ end
1799
+ end
1800
+
1801
+ def teardown
1802
+ Person.connection.drop_table :delete_me rescue nil
1803
+ end
1804
+
1805
+ def test_references_column_type_adds_id
1806
+ with_change_table do |t|
1807
+ @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
1808
+ t.references :customer
1809
+ end
1810
+ end
1811
+
1812
+ def test_remove_references_column_type_removes_id
1813
+ with_change_table do |t|
1814
+ @connection.expects(:remove_column).with(:delete_me, 'customer_id')
1815
+ t.remove_references :customer
1816
+ end
1817
+ end
1818
+
1819
+ def test_add_belongs_to_works_like_add_references
1820
+ with_change_table do |t|
1821
+ @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
1822
+ t.belongs_to :customer
1823
+ end
1824
+ end
1825
+
1826
+ def test_remove_belongs_to_works_like_remove_references
1827
+ with_change_table do |t|
1828
+ @connection.expects(:remove_column).with(:delete_me, 'customer_id')
1829
+ t.remove_belongs_to :customer
1830
+ end
1831
+ end
1832
+
1833
+ def test_references_column_type_with_polymorphic_adds_type
1834
+ with_change_table do |t|
1835
+ @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {})
1836
+ @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {})
1837
+ t.references :taggable, :polymorphic => true
1838
+ end
1839
+ end
1840
+
1841
+ def test_remove_references_column_type_with_polymorphic_removes_type
1842
+ with_change_table do |t|
1843
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
1844
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
1845
+ t.remove_references :taggable, :polymorphic => true
1846
+ end
1847
+ end
1848
+
1849
+ def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
1850
+ with_change_table do |t|
1851
+ @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {:null => false})
1852
+ @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {:null => false})
1853
+ t.references :taggable, :polymorphic => true, :null => false
1854
+ end
1855
+ end
1856
+
1857
+ def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
1858
+ with_change_table do |t|
1859
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
1860
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
1861
+ t.remove_references :taggable, :polymorphic => true, :null => false
1862
+ end
1863
+ end
1864
+
1865
+ def test_timestamps_creates_updated_at_and_created_at
1866
+ with_change_table do |t|
1867
+ @connection.expects(:add_timestamps).with(:delete_me)
1868
+ t.timestamps
1869
+ end
1870
+ end
1871
+
1872
+ def test_remove_timestamps_creates_updated_at_and_created_at
1873
+ with_change_table do |t|
1874
+ @connection.expects(:remove_timestamps).with(:delete_me)
1875
+ t.remove_timestamps
1876
+ end
1877
+ end
1878
+
1879
+ def string_column
1880
+ if current_adapter?(:PostgreSQLAdapter)
1881
+ "character varying(255)"
1882
+ elsif current_adapter?(:OracleAdapter)
1883
+ 'VARCHAR2(255)'
1884
+ else
1885
+ 'varchar(255)'
1886
+ end
1887
+ end
1888
+
1889
+ def integer_column
1890
+ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
1891
+ 'int(11)'
1892
+ elsif current_adapter?(:OracleAdapter)
1893
+ 'NUMBER(38)'
1894
+ else
1895
+ 'integer'
1896
+ end
1897
+ end
1898
+
1899
+ def test_integer_creates_integer_column
1900
+ with_change_table do |t|
1901
+ @connection.expects(:add_column).with(:delete_me, :foo, integer_column, {})
1902
+ @connection.expects(:add_column).with(:delete_me, :bar, integer_column, {})
1903
+ t.integer :foo, :bar
1904
+ end
1905
+ end
1906
+
1907
+ def test_string_creates_string_column
1908
+ with_change_table do |t|
1909
+ @connection.expects(:add_column).with(:delete_me, :foo, string_column, {})
1910
+ @connection.expects(:add_column).with(:delete_me, :bar, string_column, {})
1911
+ t.string :foo, :bar
1912
+ end
1913
+ end
1914
+
1915
+ def test_column_creates_column
1916
+ with_change_table do |t|
1917
+ @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
1918
+ t.column :bar, :integer
1919
+ end
1920
+ end
1921
+
1922
+ def test_column_creates_column_with_options
1923
+ with_change_table do |t|
1924
+ @connection.expects(:add_column).with(:delete_me, :bar, :integer, {:null => false})
1925
+ t.column :bar, :integer, :null => false
1926
+ end
1927
+ end
1928
+
1929
+ def test_index_creates_index
1930
+ with_change_table do |t|
1931
+ @connection.expects(:add_index).with(:delete_me, :bar, {})
1932
+ t.index :bar
1933
+ end
1934
+ end
1935
+
1936
+ def test_index_creates_index_with_options
1937
+ with_change_table do |t|
1938
+ @connection.expects(:add_index).with(:delete_me, :bar, {:unique => true})
1939
+ t.index :bar, :unique => true
1940
+ end
1941
+ end
1942
+
1943
+ def test_index_exists
1944
+ with_change_table do |t|
1945
+ @connection.expects(:index_exists?).with(:delete_me, :bar, {})
1946
+ t.index_exists?(:bar)
1947
+ end
1948
+ end
1949
+
1950
+ def test_index_exists_with_options
1951
+ with_change_table do |t|
1952
+ @connection.expects(:index_exists?).with(:delete_me, :bar, {:unique => true})
1953
+ t.index_exists?(:bar, :unique => true)
1954
+ end
1955
+ end
1956
+
1957
+ def test_change_changes_column
1958
+ with_change_table do |t|
1959
+ @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
1960
+ t.change :bar, :string
1961
+ end
1962
+ end
1963
+
1964
+ def test_change_changes_column_with_options
1965
+ with_change_table do |t|
1966
+ @connection.expects(:change_column).with(:delete_me, :bar, :string, {:null => true})
1967
+ t.change :bar, :string, :null => true
1968
+ end
1969
+ end
1970
+
1971
+ def test_change_default_changes_column
1972
+ with_change_table do |t|
1973
+ @connection.expects(:change_column_default).with(:delete_me, :bar, :string)
1974
+ t.change_default :bar, :string
1975
+ end
1976
+ end
1977
+
1978
+ def test_remove_drops_single_column
1979
+ with_change_table do |t|
1980
+ @connection.expects(:remove_column).with(:delete_me, [:bar])
1981
+ t.remove :bar
1982
+ end
1983
+ end
1984
+
1985
+ def test_remove_drops_multiple_columns
1986
+ with_change_table do |t|
1987
+ @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
1988
+ t.remove :bar, :baz
1989
+ end
1990
+ end
1991
+
1992
+ def test_remove_index_removes_index_with_options
1993
+ with_change_table do |t|
1994
+ @connection.expects(:remove_index).with(:delete_me, {:unique => true})
1995
+ t.remove_index :unique => true
1996
+ end
1997
+ end
1998
+
1999
+ def test_rename_renames_column
2000
+ with_change_table do |t|
2001
+ @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
2002
+ t.rename :bar, :baz
2003
+ end
2004
+ end
2005
+
2006
+ protected
2007
+ def with_change_table
2008
+ Person.connection.change_table :delete_me do |t|
2009
+ yield t
2010
+ end
2011
+ end
2012
+ end
2013
+ end
2014
+