odbc-rails 1.3 → 1.4

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 (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
+