ibm_db 2.5.6-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +123 -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
+