odbc-rails 1.3 → 1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/ChangeLog +18 -0
  2. data/NEWS +5 -0
  3. data/README +20 -7
  4. data/lib/active_record/connection_adapters/odbc_adapter.rb +254 -211
  5. data/lib/active_record/vendor/odbcext_informix.rb +17 -4
  6. data/lib/active_record/vendor/odbcext_ingres.rb +5 -5
  7. data/lib/active_record/vendor/odbcext_microsoftsqlserver.rb +35 -4
  8. data/lib/active_record/vendor/odbcext_mysql.rb +36 -8
  9. data/lib/active_record/vendor/odbcext_oracle.rb +4 -4
  10. data/lib/active_record/vendor/odbcext_postgresql.rb +8 -12
  11. data/lib/active_record/vendor/odbcext_progress.rb +3 -3
  12. data/lib/active_record/vendor/odbcext_progress89.rb +5 -4
  13. data/lib/active_record/vendor/odbcext_sybase.rb +6 -5
  14. data/lib/active_record/vendor/odbcext_virtuoso.rb +16 -3
  15. data/support/odbc_rails.diff +335 -266
  16. data/support/rake_fixes/README +6 -0
  17. data/support/rake_fixes/databases.dif +13 -0
  18. data/support/test/base_test.rb +333 -75
  19. data/support/test/migration_test.rb +430 -149
  20. data/test/connections/native_odbc/connection.rb +63 -44
  21. data/test/fixtures/db_definitions/db2.drop.sql +1 -0
  22. data/test/fixtures/db_definitions/db2.sql +9 -0
  23. data/test/fixtures/db_definitions/informix.drop.sql +1 -0
  24. data/test/fixtures/db_definitions/informix.sql +10 -0
  25. data/test/fixtures/db_definitions/ingres.drop.sql +2 -0
  26. data/test/fixtures/db_definitions/ingres.sql +9 -0
  27. data/test/fixtures/db_definitions/mysql.drop.sql +1 -0
  28. data/test/fixtures/db_definitions/mysql.sql +21 -12
  29. data/test/fixtures/db_definitions/oracle_odbc.drop.sql +4 -0
  30. data/test/fixtures/db_definitions/oracle_odbc.sql +29 -1
  31. data/test/fixtures/db_definitions/postgresql.drop.sql +3 -1
  32. data/test/fixtures/db_definitions/postgresql.sql +13 -3
  33. data/test/fixtures/db_definitions/progress.drop.sql +2 -0
  34. data/test/fixtures/db_definitions/progress.sql +11 -0
  35. data/test/fixtures/db_definitions/sqlserver.drop.sql +3 -0
  36. data/test/fixtures/db_definitions/sqlserver.sql +35 -0
  37. data/test/fixtures/db_definitions/sybase.drop.sql +2 -0
  38. data/test/fixtures/db_definitions/sybase.sql +16 -7
  39. data/test/fixtures/db_definitions/virtuoso.drop.sql +1 -0
  40. data/test/fixtures/db_definitions/virtuoso.sql +10 -0
  41. metadata +6 -3
@@ -1,9 +1,15 @@
1
1
  require 'abstract_unit'
2
+ require 'bigdecimal/util'
3
+
2
4
  require 'fixtures/person'
5
+ require 'fixtures/topic'
3
6
  require File.dirname(__FILE__) + '/fixtures/migrations/1_people_have_last_names'
4
7
  require File.dirname(__FILE__) + '/fixtures/migrations/2_we_need_reminders'
8
+ require File.dirname(__FILE__) + '/fixtures/migrations_with_decimal/1_give_me_big_numbers'
9
+
10
+ if ActiveRecord::Base.connection.supports_migrations?
11
+ class BigNumber < ActiveRecord::Base; end
5
12
 
6
- if ActiveRecord::Base.connection.supports_migrations?
7
13
  class Reminder < ActiveRecord::Base; end
8
14
 
9
15
  class ActiveRecord::Migration
@@ -28,20 +34,18 @@ if ActiveRecord::Base.connection.supports_migrations?
28
34
  ActiveRecord::Base.connection.initialize_schema_information
29
35
  ActiveRecord::Base.connection.update "UPDATE #{ActiveRecord::Migrator.schema_info_table_name} SET version = 0"
30
36
 
31
- Reminder.connection.drop_table("reminders") rescue nil
32
- Reminder.connection.drop_table("people_reminders") rescue nil
33
- Reminder.connection.drop_table("prefix_reminders_suffix") rescue nil
37
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
38
+ Reminder.connection.drop_table(table) rescue nil
39
+ end
34
40
  Reminder.reset_column_information
35
41
 
36
- Person.connection.remove_column("people", "last_name") rescue nil
37
- Person.connection.remove_column("people", "key") rescue nil
38
- Person.connection.remove_column("people", "bio") rescue nil
39
- Person.connection.remove_column("people", "age") rescue nil
40
- Person.connection.remove_column("people", "height") rescue nil
41
- Person.connection.remove_column("people", "birthday") rescue nil
42
- Person.connection.remove_column("people", "favorite_day") rescue nil
43
- Person.connection.remove_column("people", "male") rescue nil
44
- Person.connection.remove_column("people", "administrator") rescue nil
42
+ %w(last_name key bio age height wealth birthday favorite_day
43
+ male administrator).each do |column|
44
+ Person.connection.remove_column('people', column) rescue nil
45
+ end
46
+ Person.connection.remove_column("people", "first_name") rescue nil
47
+ Person.connection.remove_column("people", "middle_name") rescue nil
48
+ Person.connection.add_column("people", "first_name", :string, :limit => 40)
45
49
  Person.reset_column_information
46
50
  end
47
51
 
@@ -52,23 +56,35 @@ if ActiveRecord::Base.connection.supports_migrations?
52
56
  # Informix may return error -517: "The total size of the index is too large..."
53
57
  Person.connection.add_column "people", "last_name", :string, {:limit => 40}
54
58
  else
55
- Person.connection.add_column "people", "last_name", :string
59
+ # Limit size of last_name and key columns to support Firebird index limitations
60
+ Person.connection.add_column "people", "last_name", :string, :limit => 100
56
61
  end
62
+ Person.connection.add_column "people", "key", :string, :limit => 100
57
63
  Person.connection.add_column "people", "administrator", :boolean
58
- Person.connection.add_column "people", "key", :string
59
-
64
+
60
65
  assert_nothing_raised { Person.connection.add_index("people", "last_name") }
61
66
  assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
62
67
 
63
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
64
- assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
65
-
68
+ # Orcl nds shrt indx nms. Sybs 2.
69
+ unless current_adapter?(:OracleAdapter, :SybaseAdapter) ||
70
+ current_adapter?(:ODBCAdapter) && [:sybase, :oracle].include?(ActiveRecord::Base.connection.dbmsName)
71
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
72
+ assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
73
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
74
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
75
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
76
+ assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
77
+ assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
78
+ assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
79
+ end
80
+
66
81
  # quoting
67
- assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key", :unique => true) }
68
- assert_nothing_raised { Person.connection.remove_index("people", :name => "key") }
82
+ # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
83
+ assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
84
+ assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
69
85
 
70
86
  # Sybase adapter does not support indexes on :boolean columns
71
- unless current_adapter?(:SybaseAdapter) ||
87
+ unless current_adapter?(:SybaseAdapter) ||
72
88
  current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :sybase
73
89
  assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
74
90
  assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
@@ -87,8 +103,10 @@ if ActiveRecord::Base.connection.supports_migrations?
87
103
  end
88
104
 
89
105
  def test_create_table_with_not_null_column
90
- Person.connection.create_table :testings do |t|
91
- t.column :foo, :string, :null => false
106
+ assert_nothing_raised do
107
+ Person.connection.create_table :testings do |t|
108
+ t.column :foo, :string, :null => false
109
+ end
92
110
  end
93
111
 
94
112
  assert_raises(ActiveRecord::StatementInvalid) do
@@ -113,38 +131,62 @@ if ActiveRecord::Base.connection.supports_migrations?
113
131
  four = columns.detect { |c| c.name == "four" }
114
132
 
115
133
  assert_equal "hello", one.default
116
- if current_adapter?(:OracleAdapter)
117
- # Oracle doesn't support native booleans
118
- assert_equal true, two.default == 1
119
- assert_equal false, three.default != 0
120
- elsif current_adapter?(:ODBCAdapter) &&
121
- [:informix, :ingres, :virtuoso, :oracle, :mysql, :microsoftsqlserver].include?(ActiveRecord::Base.connection.dbmsName)
122
- # Above databases/ODBC drivers don't support native booleans.
123
- # They use an integer type instead.
124
- assert_equal true, two.default == 1
125
- assert_equal false, three.default != 0
126
- else
127
- assert_equal true, two.default
128
- assert_equal false, three.default
129
- end
134
+ assert_equal true, two.default
135
+ assert_equal false, three.default
130
136
  assert_equal 1, four.default
131
137
 
132
138
  ensure
133
139
  Person.connection.drop_table :testings rescue nil
134
140
  end
135
-
141
+
142
+ def test_create_table_with_limits
143
+ assert_nothing_raised do
144
+ Person.connection.create_table :testings do |t|
145
+ t.column :foo, :string, :limit => 255
146
+
147
+ t.column :default_int, :integer
148
+
149
+ t.column :one_int, :integer, :limit => 1
150
+ t.column :four_int, :integer, :limit => 4
151
+ t.column :eight_int, :integer, :limit => 8
152
+ end
153
+ end
154
+
155
+ columns = Person.connection.columns(:testings)
156
+ foo = columns.detect { |c| c.name == "foo" }
157
+ assert_equal 255, foo.limit
158
+
159
+ default = columns.detect { |c| c.name == "default_int" }
160
+ one = columns.detect { |c| c.name == "one_int" }
161
+ four = columns.detect { |c| c.name == "four_int" }
162
+ eight = columns.detect { |c| c.name == "eight_int" }
163
+
164
+ if current_adapter?(:PostgreSQLAdapter)
165
+ assert_equal 'integer', default.sql_type
166
+ assert_equal 'smallint', one.sql_type
167
+ assert_equal 'integer', four.sql_type
168
+ assert_equal 'bigint', eight.sql_type
169
+ elsif current_adapter?(:OracleAdapter)
170
+ assert_equal 'NUMBER(38)', default.sql_type
171
+ assert_equal 'NUMBER(1)', one.sql_type
172
+ assert_equal 'NUMBER(4)', four.sql_type
173
+ assert_equal 'NUMBER(8)', eight.sql_type
174
+ end
175
+ ensure
176
+ Person.connection.drop_table :testings rescue nil
177
+ end
178
+
136
179
  # SQL Server and Sybase will not allow you to add a NOT NULL column
137
180
  # to a table without specifying a default value, so the
138
- # following test must be skipped
139
- unless current_adapter?(:SQLServerAdapter) || current_adapter?(:SybaseAdapter) ||
181
+ # following test must be skipped
182
+ unless current_adapter?(:SQLServerAdapter, :SybaseAdapter) ||
140
183
  current_adapter?(:ODBCAdapter) && [:microsoftsqlserver, :sybase].include?(ActiveRecord::Base.connection.dbmsName)
141
184
  def test_add_column_not_null_without_default
142
-
143
185
  Person.connection.create_table :testings do |t|
144
- t.column :foo, :string
186
+ t.column :foo, :string
145
187
  end
146
- Person.connection.add_column :testings, :bar, :string, :null => false
147
-
188
+ Person.connection.add_column :testings, :bar, :string, :null => false
189
+
148
190
  assert_raises(ActiveRecord::StatementInvalid) do
149
191
  Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
150
192
  end
@@ -152,53 +194,111 @@ if ActiveRecord::Base.connection.supports_migrations?
152
194
  Person.connection.drop_table :testings rescue nil
153
195
  end
154
196
  end
155
-
197
+
156
198
  def test_add_column_not_null_with_default
157
199
  Person.connection.create_table :testings do |t|
158
200
  t.column :foo, :string
159
201
  end
160
- if current_adapter?(:ODBCAdapter) && [:ingres].include?(ActiveRecord::Base.connection.dbmsName)
202
+
203
+ con = Person.connection
204
+ Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
205
+ Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
206
+ Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
207
+ if current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :ingres
161
208
  # Ingres requires that if 'ALTER TABLE table ADD column' specifies a NOT NULL constraint,
162
209
  # then 'WITH DEFAULT' must also be specified *without* a default value.
163
- Person.connection.add_column :testings, :bar, :string, :null => false
210
+ assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false}
164
211
  else
165
- Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default"
212
+ assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default"}
166
213
  end
167
-
214
+
168
215
  assert_raises(ActiveRecord::StatementInvalid) do
169
- Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
216
+ 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)"
170
217
  end
171
218
  ensure
172
219
  Person.connection.drop_table :testings rescue nil
173
220
  end
174
-
221
+
222
+ # We specifically do a manual INSERT here, and then test only the SELECT
223
+ # functionality. This allows us to more easily catch INSERT being broken,
224
+ # but SELECT actually working fine.
225
+ def test_native_decimal_insert_manual_vs_automatic
226
+ # SQLite3 always uses float in violation of SQL
227
+ # 16 decimal places
228
+ correct_value = (current_adapter?(:SQLiteAdapter) ? '0.123456789012346E20' : '0012345678901234567890.0123456789').to_d
229
+
230
+ Person.delete_all
231
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
232
+ Person.reset_column_information
233
+
234
+ # Do a manual insertion
235
+ if current_adapter?(:OracleAdapter)
236
+ Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
237
+ else
238
+ Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
239
+ end
240
+
241
+ # SELECT
242
+ row = Person.find(:first)
243
+ assert_kind_of BigDecimal, row.wealth
244
+
245
+ # If this assert fails, that means the SELECT is broken!
246
+ assert_equal correct_value, row.wealth
247
+
248
+ # Reset to old state
249
+ Person.delete_all
250
+
251
+ # Now use the Rails insertion
252
+ assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
253
+
254
+ # SELECT
255
+ row = Person.find(:first)
256
+ assert_kind_of BigDecimal, row.wealth
257
+
258
+ # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
259
+ assert_equal correct_value, row.wealth
260
+
261
+ # Reset to old state
262
+ Person.connection.del_column "people", "wealth" rescue nil
263
+ Person.reset_column_information
264
+ end
265
+
175
266
  def test_native_types
176
267
  Person.delete_all
177
268
  Person.connection.add_column "people", "last_name", :string
178
269
  Person.connection.add_column "people", "bio", :text
179
270
  Person.connection.add_column "people", "age", :integer
180
271
  Person.connection.add_column "people", "height", :float
272
+ Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
181
273
  Person.connection.add_column "people", "birthday", :datetime
182
274
  Person.connection.add_column "people", "favorite_day", :date
183
275
  Person.connection.add_column "people", "male", :boolean
184
- assert_nothing_raised { Person.create :first_name => 'bob', :last_name => 'bobsen', :bio => "I was born ....", :age => 18, :height => 1.78, :birthday => 18.years.ago, :favorite_day => 10.days.ago, :male => true }
276
+ assert_nothing_raised { Person.create :first_name => 'bob', :last_name => 'bobsen', :bio => "I was born ....", :age => 18, :height => 1.78, :wealth => BigDecimal.new("12345678901234567890.0123456789"), :birthday => 18.years.ago, :favorite_day => 10.days.ago, :male => true }
185
277
  bob = Person.find(:first)
186
-
187
- assert_equal bob.first_name, 'bob'
188
- assert_equal bob.last_name, 'bobsen'
189
- assert_equal bob.bio, "I was born ...."
190
- assert_equal bob.age, 18
191
- assert_equal bob.male?, true
192
-
278
+
279
+ assert_equal 'bob', bob.first_name
280
+ assert_equal 'bobsen', bob.last_name
281
+ assert_equal "I was born ....", bob.bio
282
+ assert_equal 18, bob.age
283
+
284
+ # Test for 30 significent digits (beyond the 16 of float), 10 of them
285
+ # after the decimal place.
286
+ if current_adapter?(:SQLiteAdapter)
287
+ # SQLite3 uses float in violation of SQL. Test for 16 decimal places.
288
+ assert_equal BigDecimal.new('0.123456789012346E20'), bob.wealth
289
+ else
290
+ assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
291
+ end
292
+
293
+ assert_equal true, bob.male?
294
+
193
295
  assert_equal String, bob.first_name.class
194
296
  assert_equal String, bob.last_name.class
195
297
  assert_equal String, bob.bio.class
196
298
  assert_equal Fixnum, bob.age.class
197
299
  assert_equal Time, bob.birthday.class
198
300
 
199
- if current_adapter?(:SQLServerAdapter) ||
200
- current_adapter?(:OracleAdapter) ||
201
- current_adapter?(:SybaseAdapter) ||
301
+ if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter) ||
202
302
  (current_adapter?(:ODBCAdapter) &&
203
303
  [:ingres, :oracle, :microsoftsqlserver].include?(ActiveRecord::Base.connection.dbmsName))
204
304
  # SQL Server, Sybase, Oracle and Ingres don't differentiate between date/time
@@ -208,16 +308,17 @@ if ActiveRecord::Base.connection.supports_migrations?
208
308
  end
209
309
 
210
310
  assert_equal TrueClass, bob.male?.class
311
+ assert_kind_of BigDecimal, bob.wealth
211
312
  end
212
313
 
213
314
  def test_add_remove_single_field_using_string_arguments
214
315
  assert !Person.column_methods_hash.include?(:last_name)
215
-
316
+
216
317
  ActiveRecord::Migration.add_column 'people', 'last_name', :string
217
318
 
218
319
  Person.reset_column_information
219
320
  assert Person.column_methods_hash.include?(:last_name)
220
-
321
+
221
322
  ActiveRecord::Migration.remove_column 'people', 'last_name'
222
323
 
223
324
  Person.reset_column_information
@@ -237,35 +338,31 @@ if ActiveRecord::Base.connection.supports_migrations?
237
338
  Person.reset_column_information
238
339
  assert !Person.column_methods_hash.include?(:last_name)
239
340
  end
240
-
341
+
241
342
  # Ingres, Virtuoso:
242
343
  # Neither supports renaming of columns. Skip test.
243
344
  unless current_adapter?(:ODBCAdapter) &&
244
345
  [:ingres, :virtuoso].include?(ActiveRecord::Base.connection.dbmsName)
245
346
  def test_add_rename
246
347
  Person.delete_all
247
-
348
+
248
349
  begin
249
- # Some DBs complain girlfriend column already exists on two consecutive add_column calls
250
- unless current_adapter?(:ODBCAdapter) && [:informix, :oracle, :mysql, :microsoftsqlserver, :sybase, :postgresql].include?(ActiveRecord::Base.connection.dbmsName)
251
- Person.connection.add_column "people", "girlfriend", :string
252
- end
253
- Person.connection.add_column "people", "girlfriend", :string, :limit => 40
254
- Person.create :girlfriend => 'bobette'
255
-
350
+ Person.connection.add_column "people", "girlfriend", :string
351
+ Person.create :girlfriend => 'bobette'
352
+
256
353
  Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
257
-
258
- Person.reset_column_information
354
+
355
+ Person.reset_column_information
259
356
  bob = Person.find(:first)
260
-
357
+
261
358
  assert_equal "bobette", bob.exgirlfriend
262
359
  ensure
263
360
  Person.connection.remove_column("people", "girlfriend") rescue nil
264
- Person.connection.remove_column("people", "exgirlfriend") rescue nil
265
- end
361
+ Person.connection.remove_column("people", "exgirlfriend") rescue nil
362
+ end
266
363
  end
267
364
  end
268
-
365
+
269
366
  # Ingres and Virtuoso don't support renaming of columns. Skip test.
270
367
  unless current_adapter?(:ODBCAdapter) && [:ingres, :virtuoso].include?(ActiveRecord::Base.connection.dbmsName)
271
368
  def test_rename_column_using_symbol_arguments
@@ -279,8 +376,7 @@ if ActiveRecord::Base.connection.supports_migrations?
279
376
  end
280
377
  end
281
378
  end
282
-
283
- # Ingres and Virtuoso don't support renaming of columns. Skip test.
379
+
284
380
  unless current_adapter?(:ODBCAdapter) && [:ingres, :virtuoso].include?(ActiveRecord::Base.connection.dbmsName)
285
381
  def test_rename_column
286
382
  begin
@@ -293,31 +389,53 @@ if ActiveRecord::Base.connection.supports_migrations?
293
389
  end
294
390
  end
295
391
  end
296
-
392
+
297
393
  # Ingres doesn't support renaming of tables. Skip test.
298
- unless current_adapter?(:ODBCAdapter) && [:ingres].include?(ActiveRecord::Base.connection.dbmsName)
394
+ unless current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :ingres
299
395
  def test_rename_table
300
396
  begin
301
397
  ActiveRecord::Base.connection.create_table :octopuses do |t|
302
398
  t.column :url, :string
303
399
  end
304
400
  ActiveRecord::Base.connection.rename_table :octopuses, :octopi
305
-
306
- assert_nothing_raised do
307
- if current_adapter?(:OracleAdapter) ||
308
- current_adapter?(:ODBCAdapter) && [:oracle].include?(ActiveRecord::Base.connection.dbmsName)
309
- # Oracle requires the explicit sequence value for the pk
310
- ActiveRecord::Base.connection.execute "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
311
- else
312
- ActiveRecord::Base.connection.execute "INSERT INTO octopi (url) VALUES ('http://www.foreverflying.com/octopus-black7.jpg')"
313
- end
401
+
402
+ # Using explicit id in insert for compatibility across all databases
403
+ con = ActiveRecord::Base.connection
404
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
405
+ 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')" }
406
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
407
+
408
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
409
+
410
+ ensure
411
+ ActiveRecord::Base.connection.drop_table :octopuses rescue nil
412
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
413
+ end
414
+ end
415
+ end
416
+
417
+ # Ingres doesn't support renaming of tables. Skip test.
418
+ unless current_adapter?(:ODBCAdapter) && ActiveRecord::Base.connection.dbmsName == :ingres
419
+ def test_rename_table_with_an_index
420
+ begin
421
+ ActiveRecord::Base.connection.create_table :octopuses do |t|
422
+ t.column :url, :string
314
423
  end
315
-
424
+ ActiveRecord::Base.connection.add_index :octopuses, :url
425
+
426
+ ActiveRecord::Base.connection.rename_table :octopuses, :octopi
427
+
428
+ # Using explicit id in insert for compatibility across all databases
429
+ con = ActiveRecord::Base.connection
430
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
431
+ 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')" }
432
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
433
+
316
434
  assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
317
-
435
+ assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
318
436
  ensure
319
437
  ActiveRecord::Base.connection.drop_table :octopuses rescue nil
320
- ActiveRecord::Base.connection.drop_table :octopi rescue nil
438
+ ActiveRecord::Base.connection.drop_table :octopi rescue nil
321
439
  end
322
440
  end
323
441
  end
@@ -339,41 +457,152 @@ if ActiveRecord::Base.connection.supports_migrations?
339
457
  Person.connection.add_column 'people', 'age', initial_type
340
458
  old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
341
459
  assert old_columns.find { |c| c.name == 'age' and c.type == initial_type }
342
-
460
+
343
461
  assert_nothing_raised { Person.connection.change_column "people", "age", new_type }
344
-
462
+
345
463
  new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
346
464
  assert_nil new_columns.find { |c| c.name == 'age' and c.type == initial_type }
347
465
  assert new_columns.find { |c| c.name == 'age' and c.type == new_type }
348
- end
466
+
467
+ # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
468
+ unless current_adapter?(:ODBCAdapter) && [:sybase].include?(ActiveRecord::Base.connection.dbmsName)
469
+ old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
470
+ assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
471
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
472
+ new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
473
+ assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
474
+ assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
475
+ assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
476
+ end
477
+ end
478
+ end
479
+
480
+ # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
481
+ unless current_adapter?(:ODBCAdapter) && [:ingres, :sybase].include?(ActiveRecord::Base.connection.dbmsName)
482
+ def test_change_column_with_nil_default
483
+ Person.connection.add_column "people", "contributor", :boolean, :default => true
484
+ Person.reset_column_information
485
+ assert Person.new.contributor?
486
+
487
+ assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
488
+ Person.reset_column_information
489
+ a = Person.new.contributor
490
+ assert !Person.new.contributor?
491
+ assert_nil Person.new.contributor
492
+ ensure
493
+ Person.connection.remove_column "people", "contributor"
494
+ end
349
495
  end
350
496
 
351
497
  # Ingres doesn't support ALTER TABLE ADD COLUMN WITH NULL WITH DEFAULT.
352
498
  # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
353
499
  unless current_adapter?(:ODBCAdapter) && [:ingres, :sybase].include?(ActiveRecord::Base.connection.dbmsName)
354
500
  def test_change_column_with_new_default
355
- Person.connection.add_column "people", "administrator", :boolean, :default => 1
356
- Person.reset_column_information
501
+ Person.connection.add_column "people", "administrator", :boolean, :default => true
502
+ Person.reset_column_information
357
503
  assert Person.new.administrator?
358
-
359
- assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => 0 }
360
- Person.reset_column_information
504
+
505
+ assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
506
+ Person.reset_column_information
361
507
  assert !Person.new.administrator?
362
- end
508
+ end
509
+ end
510
+
511
+ # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
512
+ unless current_adapter?(:ODBCAdapter) && [:sybase].include?(ActiveRecord::Base.connection.dbmsName)
513
+ def test_change_column_default
514
+ Person.connection.change_column_default "people", "first_name", "Tester"
515
+ Person.reset_column_information
516
+ assert_equal "Tester", Person.new.first_name
517
+ end
518
+ end
519
+
520
+ # Sybase ASE's ALTER TABLE doesn't support altering a column's DEFAULT definition.
521
+ unless current_adapter?(:ODBCAdapter) && [:sybase].include?(ActiveRecord::Base.connection.dbmsName)
522
+ def test_change_column_default_to_null
523
+ Person.connection.change_column_default "people", "first_name", nil
524
+ Person.reset_column_information
525
+ assert_nil Person.new.first_name
526
+ end
363
527
  end
364
528
 
365
529
  def test_add_table
366
530
  assert !Reminder.table_exists?
367
-
531
+
368
532
  WeNeedReminders.up
369
-
533
+
370
534
  assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
371
535
  assert_equal "hello world", Reminder.find(:first).content
372
-
536
+
373
537
  WeNeedReminders.down
374
538
  assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
375
539
  end
376
540
 
541
+ def test_add_table_with_decimals
542
+ Person.connection.drop_table :big_numbers rescue nil
543
+
544
+ assert !BigNumber.table_exists?
545
+ GiveMeBigNumbers.up
546
+
547
+ assert BigNumber.create(
548
+ :bank_balance => 1586.43,
549
+ :big_bank_balance => BigDecimal("1000234000567.95"),
550
+ :world_population => 6000000000,
551
+ :my_house_population => 3,
552
+ :value_of_e => BigDecimal("2.7182818284590452353602875")
553
+ )
554
+
555
+ b = BigNumber.find(:first)
556
+ assert_not_nil b
557
+
558
+ assert_not_nil b.bank_balance
559
+ assert_not_nil b.big_bank_balance
560
+ assert_not_nil b.world_population
561
+ assert_not_nil b.my_house_population
562
+ assert_not_nil b.value_of_e
563
+
564
+ # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
565
+ # is_a?(Bignum)
566
+ assert_kind_of Integer, b.world_population
567
+ assert_equal 6000000000, b.world_population
568
+ assert_kind_of Fixnum, b.my_house_population
569
+ assert_equal 3, b.my_house_population
570
+ assert_kind_of BigDecimal, b.bank_balance
571
+ assert_equal BigDecimal("1586.43"), b.bank_balance
572
+ assert_kind_of BigDecimal, b.big_bank_balance
573
+ assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
574
+
575
+ # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
576
+ # precision/scale explictly left out. By the SQL standard, numbers
577
+ # assigned to this field should be truncated but that's seldom respected.
578
+ if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
579
+ # - PostgreSQL changes the SQL spec on columns declared simply as
580
+ # "decimal" to something more useful: instead of being given a scale
581
+ # of 0, they take on the compile-time limit for precision and scale,
582
+ # so the following should succeed unless you have used really wacky
583
+ # compilation options
584
+ # - SQLite2 has the default behavior of preserving all data sent in,
585
+ # so this happens there too
586
+ assert_kind_of BigDecimal, b.value_of_e
587
+ assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
588
+ elsif current_adapter?(:SQLiteAdapter)
589
+ # - SQLite3 stores a float, in violation of SQL
590
+ assert_kind_of BigDecimal, b.value_of_e
591
+ assert_equal BigDecimal("2.71828182845905"), b.value_of_e
592
+ elsif current_adapter?(:SQLServer)
593
+ # - SQL Server rounds instead of truncating
594
+ assert_kind_of Fixnum, b.value_of_e
595
+ assert_equal 3, b.value_of_e
596
+ else
597
+ # - SQL standard is an integer
598
+ assert_kind_of Fixnum, b.value_of_e
599
+ assert_equal 2, b.value_of_e
600
+ end
601
+
602
+ GiveMeBigNumbers.down
603
+ assert_raises(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
604
+ end
605
+
377
606
  def test_migrator
378
607
  assert !Person.column_methods_hash.include?(:last_name)
379
608
  assert !Reminder.table_exists?
@@ -397,7 +626,7 @@ if ActiveRecord::Base.connection.supports_migrations?
397
626
  def test_migrator_one_up
398
627
  assert !Person.column_methods_hash.include?(:last_name)
399
628
  assert !Reminder.table_exists?
400
-
629
+
401
630
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
402
631
 
403
632
  Person.reset_column_information
@@ -409,17 +638,17 @@ if ActiveRecord::Base.connection.supports_migrations?
409
638
  assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
410
639
  assert_equal "hello world", Reminder.find(:first).content
411
640
  end
412
-
641
+
413
642
  def test_migrator_one_down
414
643
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
415
-
644
+
416
645
  ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
417
646
 
418
647
  Person.reset_column_information
419
648
  assert Person.column_methods_hash.include?(:last_name)
420
649
  assert !Reminder.table_exists?
421
650
  end
422
-
651
+
423
652
  def test_migrator_one_up_one_down
424
653
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
425
654
  ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
@@ -427,7 +656,7 @@ if ActiveRecord::Base.connection.supports_migrations?
427
656
  assert !Person.column_methods_hash.include?(:last_name)
428
657
  assert !Reminder.table_exists?
429
658
  end
430
-
659
+
431
660
  def test_migrator_verbosity
432
661
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
433
662
  assert PeopleHaveLastNames.message_count > 0
@@ -437,7 +666,7 @@ if ActiveRecord::Base.connection.supports_migrations?
437
666
  assert PeopleHaveLastNames.message_count > 0
438
667
  PeopleHaveLastNames.message_count = 0
439
668
  end
440
-
669
+
441
670
  def test_migrator_verbosity_off
442
671
  PeopleHaveLastNames.verbose = false
443
672
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
@@ -445,7 +674,7 @@ if ActiveRecord::Base.connection.supports_migrations?
445
674
  ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
446
675
  assert PeopleHaveLastNames.message_count.zero?
447
676
  end
448
-
677
+
449
678
  def test_migrator_going_down_due_to_version_target
450
679
  ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
451
680
  ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
@@ -474,14 +703,14 @@ if ActiveRecord::Base.connection.supports_migrations?
474
703
  ActiveRecord::Base.table_name_prefix = ""
475
704
  ActiveRecord::Base.table_name_suffix = ""
476
705
  end
477
-
706
+
478
707
  def test_proper_table_name
479
708
  assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
480
709
  assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
481
710
  assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
482
711
  Reminder.reset_table_name
483
712
  assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
484
-
713
+
485
714
  # Use the model's own prefix/suffix if a model is given
486
715
  ActiveRecord::Base.table_name_prefix = "ARprefix_"
487
716
  ActiveRecord::Base.table_name_suffix = "_ARsuffix"
@@ -492,8 +721,8 @@ if ActiveRecord::Base.connection.supports_migrations?
492
721
  Reminder.table_name_prefix = ''
493
722
  Reminder.table_name_suffix = ''
494
723
  Reminder.reset_table_name
495
-
496
- # Use AR::Base's prefix/suffix if string or symbol is given
724
+
725
+ # Use AR::Base's prefix/suffix if string or symbol is given
497
726
  ActiveRecord::Base.table_name_prefix = "prefix_"
498
727
  ActiveRecord::Base.table_name_suffix = "_suffix"
499
728
  Reminder.reset_table_name
@@ -502,7 +731,7 @@ if ActiveRecord::Base.connection.supports_migrations?
502
731
  ActiveRecord::Base.table_name_prefix = ""
503
732
  ActiveRecord::Base.table_name_suffix = ""
504
733
  Reminder.reset_table_name
505
- end
734
+ end
506
735
 
507
736
  def test_add_drop_table_with_prefix_and_suffix
508
737
  assert !Reminder.table_exists?
@@ -523,44 +752,96 @@ if ActiveRecord::Base.connection.supports_migrations?
523
752
  Reminder.reset_sequence_name
524
753
  end
525
754
 
526
- def test_create_table_with_binary_column
527
- Person.connection.drop_table :binary_testings rescue nil
528
-
529
- assert_nothing_raised {
530
- if current_adapter?(:ODBCAdapter) && [:informix, :ingres].include?(ActiveRecord::Base.connection.dbmsName)
531
- # Specifying a non-null default generates the following error:
532
- # Informix:
533
- # "Cannot specify non-null default value for blob column. (-594)"
534
- # Ingres:
535
- # "Cannot create a default on column of type 'long byte'"
536
- Person.connection.create_table :binary_testings do |t|
537
- t.column "data", :binary
755
+ # FrontBase does not support default values on BLOB/CLOB columns
756
+ unless current_adapter?(:FrontBaseAdapter)
757
+ def test_create_table_with_binary_column
758
+ Person.connection.drop_table :binary_testings rescue nil
759
+
760
+ assert_nothing_raised {
761
+ if current_adapter?(:ODBCAdapter) && [:informix, :ingres].include?(ActiveRecord::Base.connection.dbmsName)
762
+ # Specifying a non-null default generates the following error:
763
+ # Informix:
764
+ # "Cannot specify non-null default value for blob column. (-594)"
765
+ # Ingres:
766
+ # "Cannot create a default on column of type 'long byte'"
767
+ Person.connection.create_table :binary_testings do |t|
768
+ t.column "data", :binary
769
+ end
770
+ else
771
+ Person.connection.create_table :binary_testings do |t|
772
+ t.column "data", :binary, :default => "", :null => false
773
+ end
538
774
  end
539
- else
540
- Person.connection.create_table :binary_testings do |t|
541
- t.column "data", :binary, :default => "", :null => false
775
+ }
776
+
777
+ columns = Person.connection.columns(:binary_testings)
778
+ data_column = columns.detect { |c| c.name == "data" }
779
+
780
+ if current_adapter?(:OracleAdapter)
781
+ assert_equal "empty_blob()", data_column.default
782
+ elsif current_adapter?(:ODBCAdapter) && [:informix, :ingres].include?(ActiveRecord::Base.connection.dbmsName)
783
+ assert_nil data_column.default
784
+ else
785
+ assert_equal "", data_column.default
542
786
  end
543
- end
544
- }
545
-
546
- columns = Person.connection.columns(:binary_testings)
547
- data_column = columns.detect { |c| c.name == "data" }
548
787
 
549
- if current_adapter?(:OracleAdapter)
550
- assert_equal "empty_blob()", data_column.default
551
- elsif current_adapter?(:ODBCAdapter) && [:informix, :ingres].include?(ActiveRecord::Base.connection.dbmsName)
552
- assert_nil data_column.default
553
- else
554
- assert_equal "", data_column.default
788
+ Person.connection.drop_table :binary_testings rescue nil
555
789
  end
556
-
557
- Person.connection.drop_table :binary_testings rescue nil
558
790
  end
559
-
560
791
  def test_migrator_with_duplicates
561
792
  assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
562
793
  ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)
563
794
  end
564
795
  end
796
+
797
+ def test_migrator_with_missing_version_numbers
798
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 500)
799
+ assert !Person.column_methods_hash.include?(:middle_name)
800
+ assert_equal 4, ActiveRecord::Migrator.current_version
801
+
802
+ ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 2)
803
+ assert !Reminder.table_exists?
804
+ assert Person.column_methods_hash.include?(:last_name)
805
+ assert_equal 2, ActiveRecord::Migrator.current_version
806
+ end
807
+
808
+ def test_create_table_with_custom_sequence_name
809
+ return unless current_adapter? :OracleAdapter
810
+
811
+ # table name is 29 chars, the standard sequence name will
812
+ # be 33 chars and fail
813
+ assert_raises(ActiveRecord::StatementInvalid) do
814
+ begin
815
+ Person.connection.create_table :table_with_name_thats_just_ok do |t|
816
+ t.column :foo, :string, :null => false
817
+ end
818
+ ensure
819
+ Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
820
+ end
821
+ end
822
+
823
+ # should be all good w/ a custom sequence name
824
+ assert_nothing_raised do
825
+ begin
826
+ Person.connection.create_table :table_with_name_thats_just_ok,
827
+ :sequence_name => 'suitably_short_seq' do |t|
828
+ t.column :foo, :string, :null => false
829
+ end
830
+
831
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
832
+
833
+ ensure
834
+ Person.connection.drop_table :table_with_name_thats_just_ok,
835
+ :sequence_name => 'suitably_short_seq' rescue nil
836
+ end
837
+ end
838
+
839
+ # confirm the custom sequence got dropped
840
+ assert_raises(ActiveRecord::StatementInvalid) do
841
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
842
+ end
843
+ end
844
+
565
845
  end
566
846
  end
847
+