activerecord-odbc-adapter 2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/AUTHORS +16 -0
  2. data/COPYING +21 -0
  3. data/ChangeLog +139 -0
  4. data/LICENSE +5 -0
  5. data/NEWS +25 -0
  6. data/README +229 -0
  7. data/lib/active_record/connection_adapters/odbc_adapter.rb +1950 -0
  8. data/lib/active_record/vendor/odbcext_db2.rb +87 -0
  9. data/lib/active_record/vendor/odbcext_informix.rb +144 -0
  10. data/lib/active_record/vendor/odbcext_informix_col.rb +45 -0
  11. data/lib/active_record/vendor/odbcext_ingres.rb +156 -0
  12. data/lib/active_record/vendor/odbcext_microsoftsqlserver.rb +216 -0
  13. data/lib/active_record/vendor/odbcext_microsoftsqlserver_col.rb +40 -0
  14. data/lib/active_record/vendor/odbcext_mysql.rb +174 -0
  15. data/lib/active_record/vendor/odbcext_oracle.rb +219 -0
  16. data/lib/active_record/vendor/odbcext_postgresql.rb +158 -0
  17. data/lib/active_record/vendor/odbcext_progress.rb +139 -0
  18. data/lib/active_record/vendor/odbcext_progress89.rb +259 -0
  19. data/lib/active_record/vendor/odbcext_sqlanywhere.rb +115 -0
  20. data/lib/active_record/vendor/odbcext_sqlanywhere_col.rb +49 -0
  21. data/lib/active_record/vendor/odbcext_sybase.rb +213 -0
  22. data/lib/active_record/vendor/odbcext_sybase_col.rb +49 -0
  23. data/lib/active_record/vendor/odbcext_virtuoso.rb +158 -0
  24. data/lib/odbc_adapter.rb +28 -0
  25. data/support/lib/active_record/connection_adapters/abstract/schema_definitions.rb +259 -0
  26. data/support/odbc_rails.diff +367 -0
  27. data/support/pack_odbc.rb +119 -0
  28. data/support/rake/rails_plugin_package_task.rb +212 -0
  29. data/support/rake_fixes/README +6 -0
  30. data/support/rake_fixes/databases.dif +13 -0
  31. data/support/test/base_test.rb +1765 -0
  32. data/support/test/migration_test.rb +1007 -0
  33. data/test/connections/native_odbc/connection.rb +137 -0
  34. data/test/fixtures/db_definitions/db2.drop.sql +33 -0
  35. data/test/fixtures/db_definitions/db2.sql +237 -0
  36. data/test/fixtures/db_definitions/db22.drop.sql +2 -0
  37. data/test/fixtures/db_definitions/db22.sql +5 -0
  38. data/test/fixtures/db_definitions/informix.drop.sql +33 -0
  39. data/test/fixtures/db_definitions/informix.sql +223 -0
  40. data/test/fixtures/db_definitions/informix2.drop.sql +2 -0
  41. data/test/fixtures/db_definitions/informix2.sql +5 -0
  42. data/test/fixtures/db_definitions/ingres.drop.sql +68 -0
  43. data/test/fixtures/db_definitions/ingres.sql +252 -0
  44. data/test/fixtures/db_definitions/ingres2.drop.sql +2 -0
  45. data/test/fixtures/db_definitions/ingres2.sql +5 -0
  46. data/test/fixtures/db_definitions/mysql.drop.sql +33 -0
  47. data/test/fixtures/db_definitions/mysql.sql +238 -0
  48. data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
  49. data/test/fixtures/db_definitions/mysql2.sql +5 -0
  50. data/test/fixtures/db_definitions/oracle_odbc.drop.sql +72 -0
  51. data/test/fixtures/db_definitions/oracle_odbc.sql +296 -0
  52. data/test/fixtures/db_definitions/oracle_odbc2.drop.sql +2 -0
  53. data/test/fixtures/db_definitions/oracle_odbc2.sql +6 -0
  54. data/test/fixtures/db_definitions/postgresql.drop.sql +38 -0
  55. data/test/fixtures/db_definitions/postgresql.sql +267 -0
  56. data/test/fixtures/db_definitions/postgresql2.drop.sql +2 -0
  57. data/test/fixtures/db_definitions/postgresql2.sql +5 -0
  58. data/test/fixtures/db_definitions/progress.drop.sql +67 -0
  59. data/test/fixtures/db_definitions/progress.sql +255 -0
  60. data/test/fixtures/db_definitions/progress2.drop.sql +2 -0
  61. data/test/fixtures/db_definitions/progress2.sql +6 -0
  62. data/test/fixtures/db_definitions/sqlserver.drop.sql +35 -0
  63. data/test/fixtures/db_definitions/sqlserver.sql +247 -0
  64. data/test/fixtures/db_definitions/sqlserver2.drop.sql +2 -0
  65. data/test/fixtures/db_definitions/sqlserver2.sql +5 -0
  66. data/test/fixtures/db_definitions/sybase.drop.sql +35 -0
  67. data/test/fixtures/db_definitions/sybase.sql +222 -0
  68. data/test/fixtures/db_definitions/sybase2.drop.sql +4 -0
  69. data/test/fixtures/db_definitions/sybase2.sql +5 -0
  70. data/test/fixtures/db_definitions/virtuoso.drop.sql +33 -0
  71. data/test/fixtures/db_definitions/virtuoso.sql +218 -0
  72. data/test/fixtures/db_definitions/virtuoso2.drop.sql +2 -0
  73. data/test/fixtures/db_definitions/virtuoso2.sql +5 -0
  74. metadata +166 -0
@@ -0,0 +1,1007 @@
1
+ require 'abstract_unit'
2
+ require 'bigdecimal/util'
3
+
4
+ require 'fixtures/person'
5
+ require 'fixtures/topic'
6
+ require File.dirname(__FILE__) + '/fixtures/migrations/1_people_have_last_names'
7
+ require File.dirname(__FILE__) + '/fixtures/migrations/2_we_need_reminders'
8
+ require File.dirname(__FILE__) + '/fixtures/migrations_with_decimal/1_give_me_big_numbers'
9
+
10
+ if ActiveRecord::Base.connection.supports_migrations?
11
+ class BigNumber < ActiveRecord::Base; end
12
+
13
+ class Reminder < ActiveRecord::Base; end
14
+
15
+ class ActiveRecord::Migration
16
+ class <<self
17
+ attr_accessor :message_count
18
+ def puts(text="")
19
+ self.message_count ||= 0
20
+ self.message_count += 1
21
+ end
22
+ end
23
+ end
24
+
25
+ class MigrationTest < Test::Unit::TestCase
26
+ self.use_transactional_fixtures = false
27
+
28
+ fixtures :people
29
+
30
+ def setup
31
+ ActiveRecord::Migration.verbose = true
32
+ PeopleHaveLastNames.message_count = 0
33
+ end
34
+
35
+ def teardown
36
+ ActiveRecord::Base.connection.initialize_schema_information
37
+ ActiveRecord::Base.connection.update "UPDATE #{ActiveRecord::Migrator.schema_info_table_name} SET version = 0"
38
+
39
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
40
+ Reminder.connection.drop_table(table) rescue nil
41
+ end
42
+ Reminder.reset_column_information
43
+
44
+ %w(last_name key bio age height wealth birthday favorite_day
45
+ moment_of_truth male administrator funny).each do |column|
46
+ Person.connection.remove_column('people', column) rescue nil
47
+ end
48
+ Person.connection.remove_column("people", "first_name") rescue nil
49
+ Person.connection.remove_column("people", "middle_name") rescue nil
50
+ Person.connection.add_column("people", "first_name", :string, :limit => 40)
51
+ Person.reset_column_information
52
+ end
53
+
54
+ def test_add_index
55
+ if current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :informix
56
+ # Index on (last_name, first_name) exceeds max. index width supported by Informix if
57
+ # both columns are created with a default width of 255, in which case
58
+ # Informix may return error -517: "The total size of the index is too large..."
59
+ Person.connection.add_column "people", "last_name", :string, {:limit => 40}
60
+ else
61
+ # Limit size of last_name and key columns to support Firebird index limitations
62
+ Person.connection.add_column "people", "last_name", :string, :limit => 100
63
+ end
64
+ Person.connection.add_column "people", "key", :string, :limit => 100
65
+ Person.connection.add_column "people", "administrator", :boolean
66
+
67
+ assert_nothing_raised { Person.connection.add_index("people", "last_name") }
68
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
69
+
70
+ # Orcl nds shrt indx nms. Sybs 2.
71
+ # OpenBase does not have named indexes. You must specify a single column name
72
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter, :OpenBaseAdapter) ||
73
+ current_adapter?(:ODBCAdapter) && [:sybase, :oracle].include?(ActiveRecord::Base.connection.dbmsName)
74
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
75
+ assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
76
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
77
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
78
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
79
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
80
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
81
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
82
+ end
83
+
84
+ # quoting
85
+ # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
86
+ # OpenBase does not have named indexes. You must specify a single column name
87
+ unless current_adapter?(:OpenBaseAdapter)
88
+ assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
89
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
90
+ end
91
+
92
+ # Sybase adapter does not support indexes on :boolean columns
93
+ # OpenBase does not have named indexes. You must specify a single column
94
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter) ||
95
+ current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :sybase
96
+ assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
97
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
98
+ end
99
+ end
100
+
101
+ def test_create_table_adds_id
102
+ Person.connection.create_table :testings do |t|
103
+ t.column :foo, :string
104
+ end
105
+
106
+ assert_equal %w(foo id),
107
+ Person.connection.columns(:testings).map { |c| c.name }.sort
108
+ ensure
109
+ Person.connection.drop_table :testings rescue nil
110
+ end
111
+
112
+ def test_create_table_with_not_null_column
113
+ assert_nothing_raised do
114
+ Person.connection.create_table :testings do |t|
115
+ t.column :foo, :string, :null => false
116
+ end
117
+ end
118
+
119
+ assert_raises(ActiveRecord::StatementInvalid) do
120
+ Person.connection.execute "insert into testings (foo) values (NULL)"
121
+ end
122
+ ensure
123
+ Person.connection.drop_table :testings rescue nil
124
+ end
125
+
126
+ def test_create_table_with_defaults
127
+ # MySQL doesn't allow defaults on TEXT or BLOB columns.
128
+ mysql = current_adapter?(:MysqlAdapter)
129
+
130
+ Person.connection.create_table :testings do |t|
131
+ t.column :one, :string, :default => "hello"
132
+ t.column :two, :boolean, :default => true
133
+ t.column :three, :boolean, :default => false
134
+ t.column :four, :integer, :default => 1
135
+ t.column :five, :text, :default => "hello" unless mysql
136
+ end
137
+
138
+ columns = Person.connection.columns(:testings)
139
+ one = columns.detect { |c| c.name == "one" }
140
+ two = columns.detect { |c| c.name == "two" }
141
+ three = columns.detect { |c| c.name == "three" }
142
+ four = columns.detect { |c| c.name == "four" }
143
+ five = columns.detect { |c| c.name == "five" } unless mysql
144
+
145
+ assert_equal "hello", one.default
146
+ assert_equal true, two.default
147
+ assert_equal false, three.default
148
+ assert_equal 1, four.default
149
+ assert_equal "hello", five.default unless mysql
150
+
151
+ ensure
152
+ Person.connection.drop_table :testings rescue nil
153
+ end
154
+
155
+ def test_create_table_with_limits
156
+ assert_nothing_raised do
157
+ Person.connection.create_table :testings do |t|
158
+ t.column :foo, :string, :limit => 255
159
+
160
+ t.column :default_int, :integer
161
+
162
+ t.column :one_int, :integer, :limit => 1
163
+ t.column :four_int, :integer, :limit => 4
164
+ t.column :eight_int, :integer, :limit => 8
165
+ end
166
+ end
167
+
168
+ columns = Person.connection.columns(:testings)
169
+ foo = columns.detect { |c| c.name == "foo" }
170
+ assert_equal 255, foo.limit
171
+
172
+ default = columns.detect { |c| c.name == "default_int" }
173
+ one = columns.detect { |c| c.name == "one_int" }
174
+ four = columns.detect { |c| c.name == "four_int" }
175
+ eight = columns.detect { |c| c.name == "eight_int" }
176
+
177
+ if current_adapter?(:PostgreSQLAdapter)
178
+ assert_equal 'integer', default.sql_type
179
+ assert_equal 'smallint', one.sql_type
180
+ assert_equal 'integer', four.sql_type
181
+ assert_equal 'bigint', eight.sql_type
182
+ elsif current_adapter?(:OracleAdapter)
183
+ assert_equal 'NUMBER(38)', default.sql_type
184
+ assert_equal 'NUMBER(1)', one.sql_type
185
+ assert_equal 'NUMBER(4)', four.sql_type
186
+ assert_equal 'NUMBER(8)', eight.sql_type
187
+ end
188
+ ensure
189
+ Person.connection.drop_table :testings rescue nil
190
+ end
191
+
192
+ # SQL Server, Sybase, and SQLite3 will not allow you to add a NOT NULL
193
+ # column to a table without a default value.
194
+ unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :SQLiteAdapter) ||
195
+ current_adapter?(:ODBCAdapter) && [:microsoftsqlserver, :sybase].include?(ActiveRecord::Base.connection.dbmsName)
196
+ def test_add_column_not_null_without_default
197
+ Person.connection.create_table :testings do |t|
198
+ t.column :foo, :string
199
+ end
200
+ Person.connection.add_column :testings, :bar, :string, :null => false
201
+
202
+ assert_raises(ActiveRecord::StatementInvalid) do
203
+ Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
204
+ end
205
+ ensure
206
+ Person.connection.drop_table :testings rescue nil
207
+ end
208
+ end
209
+
210
+ def test_add_column_not_null_with_default
211
+ Person.connection.create_table :testings do |t|
212
+ t.column :foo, :string
213
+ end
214
+
215
+ con = Person.connection
216
+ Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
217
+ Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
218
+ Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
219
+ if current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :ingres
220
+ # Ingres requires that if 'ALTER TABLE table ADD column' specifies a NOT NULL constraint,
221
+ # then 'WITH DEFAULT' must also be specified *without* a default value.
222
+ assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false}
223
+ else
224
+ assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
225
+ end
226
+
227
+ assert_raises(ActiveRecord::StatementInvalid) do
228
+ unless current_adapter?(:OpenBaseAdapter)
229
+ 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)"
230
+ else
231
+ 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)",
232
+ "Testing Insert","id",2)
233
+ end
234
+ end
235
+ ensure
236
+ Person.connection.drop_table :testings rescue nil
237
+ end
238
+
239
+ # We specifically do a manual INSERT here, and then test only the SELECT
240
+ # functionality. This allows us to more easily catch INSERT being broken,
241
+ # but SELECT actually working fine.
242
+ def test_native_decimal_insert_manual_vs_automatic
243
+ correct_value = '0012345678901234567890.0123456789'.to_d
244
+
245
+ Person.delete_all
246
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
247
+ Person.reset_column_information
248
+
249
+ # Do a manual insertion
250
+ if current_adapter?(:OracleAdapter)
251
+ Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
252
+ elsif current_adapter?(:OpenBaseAdapter)
253
+ Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
254
+ else
255
+ Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
256
+ end
257
+
258
+ # SELECT
259
+ row = Person.find(:first)
260
+ assert_kind_of BigDecimal, row.wealth
261
+
262
+ # If this assert fails, that means the SELECT is broken!
263
+ unless current_adapter?(:SQLite3Adapter)
264
+ assert_equal correct_value, row.wealth
265
+ end
266
+
267
+ # Reset to old state
268
+ Person.delete_all
269
+
270
+ # Now use the Rails insertion
271
+ assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
272
+
273
+ # SELECT
274
+ row = Person.find(:first)
275
+ assert_kind_of BigDecimal, row.wealth
276
+
277
+ # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
278
+ unless current_adapter?(:SQLite3Adapter)
279
+ assert_equal correct_value, row.wealth
280
+ end
281
+
282
+ # Reset to old state
283
+ Person.connection.del_column "people", "wealth" rescue nil
284
+ Person.reset_column_information
285
+ end
286
+
287
+ def test_native_types
288
+ Person.delete_all
289
+ Person.connection.add_column "people", "last_name", :string
290
+ Person.connection.add_column "people", "bio", :text
291
+ Person.connection.add_column "people", "age", :integer
292
+ Person.connection.add_column "people", "height", :float
293
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
294
+ Person.connection.add_column "people", "birthday", :datetime
295
+ Person.connection.add_column "people", "favorite_day", :date
296
+ Person.connection.add_column "people", "moment_of_truth", :datetime
297
+ Person.connection.add_column "people", "male", :boolean
298
+ Person.reset_column_information
299
+
300
+ assert_nothing_raised do
301
+ Person.create :first_name => 'bob', :last_name => 'bobsen',
302
+ :bio => "I was born ....", :age => 18, :height => 1.78,
303
+ :wealth => BigDecimal.new("12345678901234567890.0123456789"),
304
+ :birthday => 18.years.ago, :favorite_day => 10.days.ago,
305
+ :moment_of_truth => "1782-10-10 21:40:18", :male => true
306
+ end
307
+
308
+ bob = Person.find(:first)
309
+ assert_equal 'bob', bob.first_name
310
+ assert_equal 'bobsen', bob.last_name
311
+ assert_equal "I was born ....", bob.bio
312
+ assert_equal 18, bob.age
313
+
314
+ # Test for 30 significent digits (beyond the 16 of float), 10 of them
315
+ # after the decimal place.
316
+
317
+ unless current_adapter?(:SQLite3Adapter)
318
+ assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
319
+ end
320
+
321
+ assert_equal true, bob.male?
322
+
323
+ assert_equal String, bob.first_name.class
324
+ assert_equal String, bob.last_name.class
325
+ assert_equal String, bob.bio.class
326
+ assert_equal Fixnum, bob.age.class
327
+ assert_equal Time, bob.birthday.class
328
+
329
+ if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter) ||
330
+ (current_adapter?(:ODBCAdapter) &&
331
+ [:ingres, :oracle, :microsoftsqlserver].include?(ActiveRecord::Base.connection.dbmsName))
332
+ # SQL Server, Sybase, Oracle and Ingres don't differentiate between date/time
333
+ assert_equal Time, bob.favorite_day.class
334
+ else
335
+ assert_equal Date, bob.favorite_day.class
336
+ end
337
+
338
+ # Test DateTime column and defaults, including timezone.
339
+ # FIXME: moment of truth may be Time on 64-bit platforms.
340
+ if bob.moment_of_truth.is_a?(DateTime)
341
+ assert_equal DateTime.now.offset, bob.moment_of_truth.offset
342
+ assert_not_equal 0, bob.moment_of_truth.offset
343
+ assert_not_equal "Z", bob.moment_of_truth.zone
344
+ assert_equal DateTime::ITALY, bob.moment_of_truth.start
345
+ end
346
+
347
+ assert_equal TrueClass, bob.male?.class
348
+ assert_kind_of BigDecimal, bob.wealth
349
+ end
350
+
351
+ if current_adapter?(:MysqlAdapter)
352
+ def test_unabstracted_database_dependent_types
353
+ Person.delete_all
354
+
355
+ ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
356
+ Person.reset_column_information
357
+ Person.create :intelligence_quotient => 300
358
+ jonnyg = Person.find(:first)
359
+ assert_equal 127, jonnyg.intelligence_quotient
360
+ jonnyg.destroy
361
+ ensure
362
+ ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
363
+ end
364
+ end
365
+
366
+ def test_add_remove_single_field_using_string_arguments
367
+ assert !Person.column_methods_hash.include?(:last_name)
368
+
369
+ ActiveRecord::Migration.add_column 'people', 'last_name', :string
370
+
371
+ Person.reset_column_information
372
+ assert Person.column_methods_hash.include?(:last_name)
373
+
374
+ ActiveRecord::Migration.remove_column 'people', 'last_name'
375
+
376
+ Person.reset_column_information
377
+ assert !Person.column_methods_hash.include?(:last_name)
378
+ end
379
+
380
+ def test_add_remove_single_field_using_symbol_arguments
381
+ assert !Person.column_methods_hash.include?(:last_name)
382
+
383
+ ActiveRecord::Migration.add_column :people, :last_name, :string
384
+
385
+ Person.reset_column_information
386
+ assert Person.column_methods_hash.include?(:last_name)
387
+
388
+ ActiveRecord::Migration.remove_column :people, :last_name
389
+
390
+ Person.reset_column_information
391
+ assert !Person.column_methods_hash.include?(:last_name)
392
+ end
393
+
394
+ # Ingres, Virtuoso:
395
+ # Neither supports renaming of columns. Skip test.
396
+ unless current_adapter?(:ODBCAdapter) &&
397
+ [:ingres, :virtuoso].include?(ActiveRecord::Base.connection.dbmsName)
398
+ def test_add_rename
399
+ Person.delete_all
400
+
401
+ begin
402
+ Person.connection.add_column "people", "girlfriend", :string
403
+ Person.reset_column_information
404
+ Person.create :girlfriend => 'bobette'
405
+
406
+ Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
407
+
408
+ Person.reset_column_information
409
+ bob = Person.find(:first)
410
+
411
+ assert_equal "bobette", bob.exgirlfriend
412
+ ensure
413
+ Person.connection.remove_column("people", "girlfriend") rescue nil
414
+ Person.connection.remove_column("people", "exgirlfriend") rescue nil
415
+ end
416
+ end
417
+ end
418
+
419
+ # Ingres and Virtuoso don't support renaming of columns. Skip test.
420
+ unless current_adapter?(:ODBCAdapter) && [:ingres, :virtuoso].include?(ActiveRecord::Base.connection.dbmsName)
421
+ def test_rename_column_using_symbol_arguments
422
+ begin
423
+ names_before = Person.find(:all).map(&:first_name)
424
+ Person.connection.rename_column :people, :first_name, :nick_name
425
+ Person.reset_column_information
426
+ assert Person.column_names.include?("nick_name")
427
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
428
+ ensure
429
+ Person.connection.remove_column("people","nick_name")
430
+ Person.connection.add_column("people","first_name", :string)
431
+ end
432
+ end
433
+ end
434
+
435
+ unless current_adapter?(:ODBCAdapter) && [:ingres, :virtuoso].include?(ActiveRecord::Base.connection.dbmsName)
436
+ def test_rename_column
437
+ begin
438
+ names_before = Person.find(:all).map(&:first_name)
439
+ Person.connection.rename_column "people", "first_name", "nick_name"
440
+ Person.reset_column_information
441
+ assert Person.column_names.include?("nick_name")
442
+ assert_equal names_before, Person.find(:all).map(&:nick_name)
443
+ ensure
444
+ Person.connection.remove_column("people","nick_name")
445
+ Person.connection.add_column("people","first_name", :string)
446
+ end
447
+ end
448
+ end
449
+
450
+ def test_rename_column_with_sql_reserved_word
451
+ begin
452
+ assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
453
+ Person.reset_column_information
454
+ assert Person.column_names.include?("group")
455
+ ensure
456
+ Person.connection.remove_column("people", "group") rescue nil
457
+ Person.connection.add_column("people", "first_name", :string) rescue nil
458
+ end
459
+ end
460
+
461
+ def test_change_type_of_not_null_column
462
+ assert_nothing_raised do
463
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
464
+ Topic.reset_column_information
465
+
466
+ Topic.connection.change_column "topics", "written_on", :datetime, :null => false
467
+ Topic.reset_column_information
468
+ end
469
+ end
470
+
471
+ # Ingres doesn't support renaming of tables. Skip test.
472
+ unless current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :ingres
473
+ def test_rename_table
474
+ begin
475
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
476
+ t.column :url, :string
477
+ end
478
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
479
+
480
+ # Using explicit id in insert for compatibility across all databases
481
+ con = ActiveRecord::Base.connection
482
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
483
+ 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')" }
484
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
485
+
486
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
487
+
488
+ ensure
489
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
490
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
491
+ end
492
+ end
493
+ end
494
+
495
+ def test_change_column_nullability
496
+ Person.delete_all
497
+ Person.connection.add_column "people", "funny", :boolean
498
+ Person.reset_column_information
499
+ assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
500
+ Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
501
+ Person.reset_column_information
502
+ assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
503
+ Person.connection.change_column "people", "funny", :boolean, :null => true
504
+ Person.reset_column_information
505
+ assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
506
+ end
507
+
508
+ # Ingres doesn't support renaming of tables. Skip test.
509
+ unless current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :ingres
510
+ def test_rename_table_with_an_index
511
+ begin
512
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
513
+ t.column :url, :string
514
+ end
515
+ ActiveRecord::Base.connection.add_index :octopuses, :url
516
+
517
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
518
+
519
+ # Using explicit id in insert for compatibility across all databases
520
+ con = ActiveRecord::Base.connection
521
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
522
+ 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')" }
523
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
524
+
525
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
526
+ assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
527
+ ensure
528
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
529
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
530
+ end
531
+ end
532
+ end
533
+
534
+ # Virtuoso disallows virtually all column type conversions.
535
+ # Conversion between any of the native types used by the ActiveRecord generic types is not allowed.
536
+ # Skip the test.
537
+ unless current_adapter?(:ODBCAdapter) && [:virtuoso].include?(ActiveRecord::Base.connection.dbmsName)
538
+ def test_change_column
539
+ #Ingres doesn't support changing an integer column to varchar/text.
540
+ if current_adapter?(:ODBCAdapter) && [:ingres].include?(ActiveRecord::Base.connection.dbmsName)
541
+ initial_type = :integer
542
+ new_type = :float
543
+ else
544
+ initial_type = :integer
545
+ new_type = :string
546
+ end
547
+
548
+ Person.connection.add_column 'people', 'age', initial_type
549
+ old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
550
+ assert old_columns.find { |c| c.name == 'age' and c.type == initial_type }
551
+
552
+ assert_nothing_raised { Person.connection.change_column "people", "age", new_type }
553
+
554
+ new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
555
+ assert_nil new_columns.find { |c| c.name == 'age' and c.type == initial_type }
556
+ assert new_columns.find { |c| c.name == 'age' and c.type == new_type }
557
+
558
+ # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
559
+ unless current_adapter?(:ODBCAdapter) && [:sybase].include?(ActiveRecord::Base.connection.dbmsName)
560
+ old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
561
+ assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
562
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
563
+ new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
564
+ assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
565
+ assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
566
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
567
+ end
568
+ end
569
+ end
570
+
571
+ # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
572
+ unless current_adapter?(:ODBCAdapter) && [:ingres, :sybase].include?(ActiveRecord::Base.connection.dbmsName)
573
+ def test_change_column_with_nil_default
574
+ Person.connection.add_column "people", "contributor", :boolean, :default => true
575
+ Person.reset_column_information
576
+ assert Person.new.contributor?
577
+
578
+ assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
579
+ Person.reset_column_information
580
+ assert !Person.new.contributor?
581
+ assert_nil Person.new.contributor
582
+ ensure
583
+ Person.connection.remove_column("people", "contributor") rescue nil
584
+ end
585
+ end
586
+
587
+ # Ingres doesn't support ALTER TABLE ADD COLUMN WITH NULL WITH DEFAULT.
588
+ # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
589
+ unless current_adapter?(:ODBCAdapter) && [:ingres, :sybase].include?(ActiveRecord::Base.connection.dbmsName)
590
+ def test_change_column_with_new_default
591
+ Person.connection.add_column "people", "administrator", :boolean, :default => true
592
+ Person.reset_column_information
593
+ assert Person.new.administrator?
594
+
595
+ assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
596
+ Person.reset_column_information
597
+ assert !Person.new.administrator?
598
+ ensure
599
+ Person.connection.remove_column("people", "administrator") rescue nil
600
+ end
601
+ end
602
+
603
+ # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
604
+ unless current_adapter?(:ODBCAdapter) && [:sybase].include?(ActiveRecord::Base.connection.dbmsName)
605
+ def test_change_column_default
606
+ Person.connection.change_column_default "people", "first_name", "Tester"
607
+ Person.reset_column_information
608
+ assert_equal "Tester", Person.new.first_name
609
+ end
610
+ end
611
+
612
+ def test_change_column_quotes_column_names
613
+ Person.connection.create_table :testings do |t|
614
+ t.column :select, :string
615
+ end
616
+
617
+ assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }
618
+
619
+ assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
620
+ ensure
621
+ Person.connection.drop_table :testings rescue nil
622
+ end
623
+
624
+ # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
625
+ unless current_adapter?(:ODBCAdapter) && [:sybase].include?(ActiveRecord::Base.connection.dbmsName)
626
+ def test_change_column_default_to_null
627
+ Person.connection.change_column_default "people", "first_name", nil
628
+ Person.reset_column_information
629
+ assert_nil Person.new.first_name
630
+ end
631
+ end
632
+
633
+ def test_add_table
634
+ assert !Reminder.table_exists?
635
+
636
+ WeNeedReminders.up
637
+
638
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
639
+ assert_equal "hello world", Reminder.find(:first).content
640
+
641
+ WeNeedReminders.down
642
+ assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
643
+ end
644
+
645
+ def test_add_table_with_decimals
646
+ Person.connection.drop_table :big_numbers rescue nil
647
+
648
+ assert !BigNumber.table_exists?
649
+ GiveMeBigNumbers.up
650
+
651
+ assert BigNumber.create(
652
+ :bank_balance => 1586.43,
653
+ :big_bank_balance => BigDecimal("1000234000567.95"),
654
+ :world_population => 6000000000,
655
+ :my_house_population => 3,
656
+ :value_of_e => BigDecimal("2.7182818284590452353602875")
657
+ )
658
+
659
+ b = BigNumber.find(:first)
660
+ assert_not_nil b
661
+
662
+ assert_not_nil b.bank_balance
663
+ assert_not_nil b.big_bank_balance
664
+ assert_not_nil b.world_population
665
+ assert_not_nil b.my_house_population
666
+ assert_not_nil b.value_of_e
667
+
668
+ # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
669
+ # is_a?(Bignum)
670
+ assert_kind_of Integer, b.world_population
671
+ assert_equal 6000000000, b.world_population
672
+ assert_kind_of Fixnum, b.my_house_population
673
+ assert_equal 3, b.my_house_population
674
+ assert_kind_of BigDecimal, b.bank_balance
675
+ assert_equal BigDecimal("1586.43"), b.bank_balance
676
+ assert_kind_of BigDecimal, b.big_bank_balance
677
+ assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
678
+
679
+ # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
680
+ # precision/scale explicitly left out. By the SQL standard, numbers
681
+ # assigned to this field should be truncated but that's seldom respected.
682
+ if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
683
+ # - PostgreSQL changes the SQL spec on columns declared simply as
684
+ # "decimal" to something more useful: instead of being given a scale
685
+ # of 0, they take on the compile-time limit for precision and scale,
686
+ # so the following should succeed unless you have used really wacky
687
+ # compilation options
688
+ # - SQLite2 has the default behavior of preserving all data sent in,
689
+ # so this happens there too
690
+ assert_kind_of BigDecimal, b.value_of_e
691
+ assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
692
+ elsif current_adapter?(:SQLiteAdapter)
693
+ # - SQLite3 stores a float, in violation of SQL
694
+ assert_kind_of BigDecimal, b.value_of_e
695
+ assert_equal BigDecimal("2.71828182845905"), b.value_of_e
696
+ elsif current_adapter?(:SQLServer)
697
+ # - SQL Server rounds instead of truncating
698
+ assert_kind_of Fixnum, b.value_of_e
699
+ assert_equal 3, b.value_of_e
700
+ else
701
+ # - SQL standard is an integer
702
+ assert_kind_of Fixnum, b.value_of_e
703
+ assert_equal 2, b.value_of_e
704
+ end
705
+
706
+ GiveMeBigNumbers.down
707
+ assert_raises(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
708
+ end
709
+
710
+ def test_migrator
711
+ assert !Person.column_methods_hash.include?(:last_name)
712
+ assert !Reminder.table_exists?
713
+
714
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
715
+
716
+ assert_equal 3, ActiveRecord::Migrator.current_version
717
+ Person.reset_column_information
718
+ assert Person.column_methods_hash.include?(:last_name)
719
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
720
+ assert_equal "hello world", Reminder.find(:first).content
721
+
722
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/')
723
+
724
+ assert_equal 0, ActiveRecord::Migrator.current_version
725
+ Person.reset_column_information
726
+ assert !Person.column_methods_hash.include?(:last_name)
727
+ assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
728
+ end
729
+
730
+ def test_migrator_one_up
731
+ assert !Person.column_methods_hash.include?(:last_name)
732
+ assert !Reminder.table_exists?
733
+
734
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
735
+
736
+ Person.reset_column_information
737
+ assert Person.column_methods_hash.include?(:last_name)
738
+ assert !Reminder.table_exists?
739
+
740
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 2)
741
+
742
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
743
+ assert_equal "hello world", Reminder.find(:first).content
744
+ end
745
+
746
+ def test_migrator_one_down
747
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
748
+
749
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
750
+
751
+ Person.reset_column_information
752
+ assert Person.column_methods_hash.include?(:last_name)
753
+ assert !Reminder.table_exists?
754
+ end
755
+
756
+ def test_migrator_one_up_one_down
757
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
758
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
759
+
760
+ assert !Person.column_methods_hash.include?(:last_name)
761
+ assert !Reminder.table_exists?
762
+ end
763
+
764
+ def test_migrator_verbosity
765
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
766
+ assert PeopleHaveLastNames.message_count > 0
767
+ PeopleHaveLastNames.message_count = 0
768
+
769
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
770
+ assert PeopleHaveLastNames.message_count > 0
771
+ PeopleHaveLastNames.message_count = 0
772
+ end
773
+
774
+ def test_migrator_verbosity_off
775
+ PeopleHaveLastNames.verbose = false
776
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
777
+ assert PeopleHaveLastNames.message_count.zero?
778
+ ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
779
+ assert PeopleHaveLastNames.message_count.zero?
780
+ end
781
+
782
+ def test_migrator_going_down_due_to_version_target
783
+ ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
784
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
785
+
786
+ assert !Person.column_methods_hash.include?(:last_name)
787
+ assert !Reminder.table_exists?
788
+
789
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/')
790
+
791
+ Person.reset_column_information
792
+ assert Person.column_methods_hash.include?(:last_name)
793
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
794
+ assert_equal "hello world", Reminder.find(:first).content
795
+ end
796
+
797
+ def test_schema_info_table_name
798
+ ActiveRecord::Base.table_name_prefix = "prefix_"
799
+ ActiveRecord::Base.table_name_suffix = "_suffix"
800
+ Reminder.reset_table_name
801
+ assert_equal "prefix_schema_info_suffix", ActiveRecord::Migrator.schema_info_table_name
802
+ ActiveRecord::Base.table_name_prefix = ""
803
+ ActiveRecord::Base.table_name_suffix = ""
804
+ Reminder.reset_table_name
805
+ assert_equal "schema_info", ActiveRecord::Migrator.schema_info_table_name
806
+ ensure
807
+ ActiveRecord::Base.table_name_prefix = ""
808
+ ActiveRecord::Base.table_name_suffix = ""
809
+ end
810
+
811
+ def test_proper_table_name
812
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
813
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
814
+ assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
815
+ Reminder.reset_table_name
816
+ assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
817
+
818
+ # Use the model's own prefix/suffix if a model is given
819
+ ActiveRecord::Base.table_name_prefix = "ARprefix_"
820
+ ActiveRecord::Base.table_name_suffix = "_ARsuffix"
821
+ Reminder.table_name_prefix = 'prefix_'
822
+ Reminder.table_name_suffix = '_suffix'
823
+ Reminder.reset_table_name
824
+ assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
825
+ Reminder.table_name_prefix = ''
826
+ Reminder.table_name_suffix = ''
827
+ Reminder.reset_table_name
828
+
829
+ # Use AR::Base's prefix/suffix if string or symbol is given
830
+ ActiveRecord::Base.table_name_prefix = "prefix_"
831
+ ActiveRecord::Base.table_name_suffix = "_suffix"
832
+ Reminder.reset_table_name
833
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
834
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
835
+ ActiveRecord::Base.table_name_prefix = ""
836
+ ActiveRecord::Base.table_name_suffix = ""
837
+ Reminder.reset_table_name
838
+ end
839
+
840
+ def test_add_drop_table_with_prefix_and_suffix
841
+ assert !Reminder.table_exists?
842
+ ActiveRecord::Base.table_name_prefix = 'prefix_'
843
+ ActiveRecord::Base.table_name_suffix = '_suffix'
844
+ Reminder.reset_table_name
845
+ Reminder.reset_sequence_name
846
+ WeNeedReminders.up
847
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
848
+ assert_equal "hello world", Reminder.find(:first).content
849
+
850
+ WeNeedReminders.down
851
+ assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
852
+ ensure
853
+ ActiveRecord::Base.table_name_prefix = ''
854
+ ActiveRecord::Base.table_name_suffix = ''
855
+ Reminder.reset_table_name
856
+ Reminder.reset_sequence_name
857
+ end
858
+
859
+ def test_create_table_with_binary_column
860
+ Person.connection.drop_table :binary_testings rescue nil
861
+
862
+ assert_nothing_raised {
863
+ if current_adapter?(:ODBCAdapter) && [:informix, :ingres].include?(ActiveRecord::Base.connection.dbmsName)
864
+ # Specifying a non-null default generates the following error:
865
+ # Informix:
866
+ # "Cannot specify non-null default value for blob column. (-594)"
867
+ # Ingres:
868
+ # "Cannot create a default on column of type 'long byte'"
869
+ Person.connection.create_table :binary_testings do |t|
870
+ t.column "data", :binary
871
+ end
872
+ else
873
+ Person.connection.create_table :binary_testings do |t|
874
+ t.column "data", :binary, :null => false
875
+ end
876
+ end
877
+ }
878
+
879
+ columns = Person.connection.columns(:binary_testings)
880
+ data_column = columns.detect { |c| c.name == "data" }
881
+
882
+ if current_adapter?(:MysqlAdapter)
883
+ assert_equal '', data_column.default
884
+ else
885
+ assert_nil data_column.default
886
+ end
887
+
888
+ Person.connection.drop_table :binary_testings rescue nil
889
+ end
890
+
891
+ def test_migrator_with_duplicates
892
+ assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
893
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)
894
+ end
895
+ end
896
+
897
+ def test_migrator_with_missing_version_numbers
898
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 500)
899
+ assert !Person.column_methods_hash.include?(:middle_name)
900
+ assert_equal 4, ActiveRecord::Migrator.current_version
901
+
902
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 2)
903
+ Person.reset_column_information
904
+ assert !Reminder.table_exists?
905
+ assert Person.column_methods_hash.include?(:last_name)
906
+ assert_equal 2, ActiveRecord::Migrator.current_version
907
+ end
908
+
909
+ def test_create_table_with_custom_sequence_name
910
+ return unless current_adapter? :OracleAdapter
911
+
912
+ # table name is 29 chars, the standard sequence name will
913
+ # be 33 chars and fail
914
+ assert_raises(ActiveRecord::StatementInvalid) do
915
+ begin
916
+ Person.connection.create_table :table_with_name_thats_just_ok do |t|
917
+ t.column :foo, :string, :null => false
918
+ end
919
+ ensure
920
+ Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
921
+ end
922
+ end
923
+
924
+ # should be all good w/ a custom sequence name
925
+ assert_nothing_raised do
926
+ begin
927
+ Person.connection.create_table :table_with_name_thats_just_ok,
928
+ :sequence_name => 'suitably_short_seq' do |t|
929
+ t.column :foo, :string, :null => false
930
+ end
931
+
932
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
933
+
934
+ ensure
935
+ Person.connection.drop_table :table_with_name_thats_just_ok,
936
+ :sequence_name => 'suitably_short_seq' rescue nil
937
+ end
938
+ end
939
+
940
+ # confirm the custom sequence got dropped
941
+ assert_raises(ActiveRecord::StatementInvalid) do
942
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
943
+ end
944
+ end
945
+ end
946
+
947
+ uses_mocha 'Sexy migration tests' do
948
+ class SexyMigrationsTest < Test::Unit::TestCase
949
+ def test_references_column_type_adds_id
950
+ with_new_table do |t|
951
+ t.expects(:column).with('customer_id', :integer, {})
952
+ t.references :customer
953
+ end
954
+ end
955
+
956
+ def test_references_column_type_with_polymorphic_adds_type
957
+ with_new_table do |t|
958
+ t.expects(:column).with('taggable_type', :string, {})
959
+ t.expects(:column).with('taggable_id', :integer, {})
960
+ t.references :taggable, :polymorphic => true
961
+ end
962
+ end
963
+
964
+ def test_belongs_to_works_like_references
965
+ with_new_table do |t|
966
+ t.expects(:column).with('customer_id', :integer, {})
967
+ t.belongs_to :customer
968
+ end
969
+ end
970
+
971
+ def test_timestamps_creates_updated_at_and_created_at
972
+ with_new_table do |t|
973
+ t.expects(:column).with(:created_at, :datetime)
974
+ t.expects(:column).with(:updated_at, :datetime)
975
+ t.timestamps
976
+ end
977
+ end
978
+
979
+ def test_integer_creates_integer_column
980
+ with_new_table do |t|
981
+ t.expects(:column).with(:foo, 'integer', {})
982
+ t.expects(:column).with(:bar, 'integer', {})
983
+ t.integer :foo, :bar
984
+ end
985
+ end
986
+
987
+ def test_string_creates_string_column
988
+ with_new_table do |t|
989
+ t.expects(:column).with(:foo, 'string', {})
990
+ t.expects(:column).with(:bar, 'string', {})
991
+ t.string :foo, :bar
992
+ end
993
+ end
994
+
995
+ protected
996
+ def with_new_table
997
+ Person.connection.create_table :delete_me do |t|
998
+ yield t
999
+ end
1000
+ ensure
1001
+ Person.connection.drop_table :delete_me rescue nil
1002
+ end
1003
+
1004
+ end # SexyMigrationsTest
1005
+ end # uses_mocha
1006
+ end
1007
+