ibm_db 0.4.0 → 0.4.6
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.
- data/CHANGES +19 -9
- data/README +29 -20
- data/ext/extconf.rb +55 -50
- data/ext/ibm_db.c +6267 -6259
- data/ext/ruby_ibm_db.h +164 -164
- data/lib/active_record/connection_adapters/ibm_db_adapter.rb +167 -142
- data/lib/active_record/vendor/db2-i5-zOS.yaml +68 -68
- data/lib/linux32/ibm_db.so +0 -0
- data/test/adapter_test.rb +128 -0
- data/test/base_test.rb +1579 -0
- data/test/{activerecord → connections}/native_ibm_db/connection.rb +6 -7
- data/test/fixtures/db_definitions/i5/ibm_db.drop.sql +32 -0
- data/test/fixtures/db_definitions/i5/ibm_db.sql +232 -0
- data/test/fixtures/db_definitions/i5/ibm_db2.drop.sql +2 -0
- data/test/fixtures/db_definitions/i5/ibm_db2.sql +5 -0
- data/test/fixtures/db_definitions/luw/ibm_db.drop.sql +31 -0
- data/test/fixtures/db_definitions/luw/ibm_db.sql +226 -0
- data/test/fixtures/db_definitions/luw/ibm_db2.drop.sql +2 -0
- data/test/fixtures/db_definitions/luw/ibm_db2.sql +5 -0
- data/test/fixtures/db_definitions/zOS/ibm_db.drop.sql +32 -0
- data/test/fixtures/db_definitions/zOS/ibm_db.sql +284 -0
- data/test/fixtures/db_definitions/zOS/ibm_db2.drop.sql +2 -0
- data/test/fixtures/db_definitions/zOS/ibm_db2.sql +7 -0
- data/test/locking_test.rb +190 -0
- data/test/migration_test.rb +779 -0
- metadata +39 -17
@@ -0,0 +1,779 @@
|
|
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
|
+
def setup
|
29
|
+
ActiveRecord::Migration.verbose = true
|
30
|
+
PeopleHaveLastNames.message_count = 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def teardown
|
34
|
+
ActiveRecord::Base.connection.initialize_schema_information
|
35
|
+
ActiveRecord::Base.connection.update "UPDATE #{ActiveRecord::Migrator.schema_info_table_name} SET version = 0"
|
36
|
+
|
37
|
+
%w(reminders people_reminders prefix_reminders_suffix).each do |table|
|
38
|
+
Reminder.connection.drop_table(table) rescue nil
|
39
|
+
end
|
40
|
+
Reminder.reset_column_information
|
41
|
+
|
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)
|
49
|
+
Person.reset_column_information
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_add_index
|
53
|
+
# Limit size of last_name and key columns to support Firebird index limitations
|
54
|
+
Person.connection.add_column "people", "last_name", :string, :limit => 100
|
55
|
+
Person.connection.add_column "people", "key", :string, :limit => 100
|
56
|
+
Person.connection.add_column "people", "administrator", :boolean
|
57
|
+
|
58
|
+
assert_nothing_raised { Person.connection.add_index("people", "last_name") }
|
59
|
+
assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
|
60
|
+
|
61
|
+
# Orcl nds shrt indx nms. Sybs 2.
|
62
|
+
unless current_adapter?(:OracleAdapter, :SybaseAdapter, :IBM_DBAdapter)
|
63
|
+
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
|
64
|
+
assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
|
65
|
+
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
|
66
|
+
assert_nothing_raised { Person.connection.remove_index("people", :name => "index_people_on_last_name_and_first_name") }
|
67
|
+
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
|
68
|
+
assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
|
69
|
+
assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
|
70
|
+
assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# quoting
|
74
|
+
# Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
|
75
|
+
assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
|
76
|
+
assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
|
77
|
+
|
78
|
+
# Sybase adapter does not support indexes on :boolean columns
|
79
|
+
unless current_adapter?(:SybaseAdapter)
|
80
|
+
assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
|
81
|
+
assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_create_table_adds_id
|
86
|
+
Person.connection.create_table :testings do |t|
|
87
|
+
t.column :foo, :string
|
88
|
+
end
|
89
|
+
|
90
|
+
assert_equal %w(foo id),
|
91
|
+
Person.connection.columns(:testings).map { |c| c.name }.sort
|
92
|
+
ensure
|
93
|
+
Person.connection.drop_table :testings rescue nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_create_table_with_not_null_column
|
97
|
+
assert_nothing_raised do
|
98
|
+
Person.connection.create_table :testings do |t|
|
99
|
+
t.column :foo, :string, :null => false
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
assert_raises(ActiveRecord::StatementInvalid) do
|
104
|
+
Person.connection.execute "insert into testings (foo) values (NULL)"
|
105
|
+
end
|
106
|
+
ensure
|
107
|
+
Person.connection.drop_table :testings rescue nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_create_table_with_defaults
|
111
|
+
Person.connection.create_table :testings do |t|
|
112
|
+
t.column :one, :string, :default => "hello"
|
113
|
+
t.column :two, :boolean, :default => true
|
114
|
+
t.column :three, :boolean, :default => false
|
115
|
+
t.column :four, :integer, :default => 1
|
116
|
+
end
|
117
|
+
|
118
|
+
columns = Person.connection.columns(:testings)
|
119
|
+
one = columns.detect { |c| c.name == "one" }
|
120
|
+
two = columns.detect { |c| c.name == "two" }
|
121
|
+
three = columns.detect { |c| c.name == "three" }
|
122
|
+
four = columns.detect { |c| c.name == "four" }
|
123
|
+
|
124
|
+
assert_equal "hello", one.default
|
125
|
+
assert_equal true, two.default
|
126
|
+
assert_equal false, three.default
|
127
|
+
assert_equal 1, four.default
|
128
|
+
|
129
|
+
ensure
|
130
|
+
Person.connection.drop_table :testings rescue nil
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_create_table_with_limits
|
134
|
+
assert_nothing_raised do
|
135
|
+
Person.connection.create_table :testings do |t|
|
136
|
+
t.column :foo, :string, :limit => 255
|
137
|
+
|
138
|
+
t.column :default_int, :integer
|
139
|
+
|
140
|
+
t.column :one_int, :integer, :limit => 1
|
141
|
+
t.column :four_int, :integer, :limit => 4
|
142
|
+
t.column :eight_int, :integer, :limit => 8
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
columns = Person.connection.columns(:testings)
|
147
|
+
foo = columns.detect { |c| c.name == "foo" }
|
148
|
+
assert_equal 255, foo.limit
|
149
|
+
|
150
|
+
default = columns.detect { |c| c.name == "default_int" }
|
151
|
+
one = columns.detect { |c| c.name == "one_int" }
|
152
|
+
four = columns.detect { |c| c.name == "four_int" }
|
153
|
+
eight = columns.detect { |c| c.name == "eight_int" }
|
154
|
+
|
155
|
+
if current_adapter?(:PostgreSQLAdapter)
|
156
|
+
assert_equal 'integer', default.sql_type
|
157
|
+
assert_equal 'smallint', one.sql_type
|
158
|
+
assert_equal 'integer', four.sql_type
|
159
|
+
assert_equal 'bigint', eight.sql_type
|
160
|
+
elsif current_adapter?(:OracleAdapter)
|
161
|
+
assert_equal 'NUMBER(38)', default.sql_type
|
162
|
+
assert_equal 'NUMBER(1)', one.sql_type
|
163
|
+
assert_equal 'NUMBER(4)', four.sql_type
|
164
|
+
assert_equal 'NUMBER(8)', eight.sql_type
|
165
|
+
end
|
166
|
+
ensure
|
167
|
+
Person.connection.drop_table :testings rescue nil
|
168
|
+
end
|
169
|
+
|
170
|
+
# SQL Server and Sybase will not allow you to add a NOT NULL column
|
171
|
+
# to a table without specifying a default value, so the
|
172
|
+
# following test must be skipped
|
173
|
+
unless current_adapter?(:SQLServerAdapter, :SybaseAdapter, :IBM_DBAdapter)
|
174
|
+
def test_add_column_not_null_without_default
|
175
|
+
Person.connection.create_table :testings do |t|
|
176
|
+
t.column :foo, :string
|
177
|
+
end
|
178
|
+
Person.connection.add_column :testings, :bar, :string, :null => false
|
179
|
+
|
180
|
+
assert_raises(ActiveRecord::StatementInvalid) do
|
181
|
+
Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
|
182
|
+
end
|
183
|
+
ensure
|
184
|
+
Person.connection.drop_table :testings rescue nil
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def test_add_column_not_null_with_default
|
189
|
+
Person.connection.create_table :testings do |t|
|
190
|
+
t.column :foo, :string
|
191
|
+
end
|
192
|
+
|
193
|
+
con = Person.connection
|
194
|
+
Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
|
195
|
+
Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
|
196
|
+
Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
|
197
|
+
assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
|
198
|
+
|
199
|
+
assert_raises(ActiveRecord::StatementInvalid) do
|
200
|
+
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)"
|
201
|
+
end
|
202
|
+
ensure
|
203
|
+
Person.connection.drop_table :testings rescue nil
|
204
|
+
end
|
205
|
+
|
206
|
+
# We specifically do a manual INSERT here, and then test only the SELECT
|
207
|
+
# functionality. This allows us to more easily catch INSERT being broken,
|
208
|
+
# but SELECT actually working fine.
|
209
|
+
def test_native_decimal_insert_manual_vs_automatic
|
210
|
+
# SQLite3 always uses float in violation of SQL
|
211
|
+
# 16 decimal places
|
212
|
+
correct_value = (current_adapter?(:SQLiteAdapter) ? '0.123456789012346E20' : '0012345678901234567890.0123456789').to_d
|
213
|
+
|
214
|
+
Person.delete_all
|
215
|
+
Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
|
216
|
+
Person.reset_column_information
|
217
|
+
|
218
|
+
# Do a manual insertion
|
219
|
+
if current_adapter?(:OracleAdapter)
|
220
|
+
Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
|
221
|
+
else
|
222
|
+
Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
|
223
|
+
end
|
224
|
+
|
225
|
+
# SELECT
|
226
|
+
row = Person.find(:first)
|
227
|
+
assert_kind_of BigDecimal, row.wealth
|
228
|
+
|
229
|
+
# If this assert fails, that means the SELECT is broken!
|
230
|
+
assert_equal correct_value, row.wealth
|
231
|
+
|
232
|
+
# Reset to old state
|
233
|
+
Person.delete_all
|
234
|
+
|
235
|
+
# Now use the Rails insertion
|
236
|
+
assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
|
237
|
+
|
238
|
+
# SELECT
|
239
|
+
row = Person.find(:first)
|
240
|
+
assert_kind_of BigDecimal, row.wealth
|
241
|
+
|
242
|
+
# If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
|
243
|
+
assert_equal correct_value, row.wealth
|
244
|
+
|
245
|
+
# Reset to old state
|
246
|
+
Person.connection.del_column "people", "wealth" rescue nil
|
247
|
+
Person.reset_column_information
|
248
|
+
end
|
249
|
+
|
250
|
+
def test_native_types
|
251
|
+
Person.delete_all
|
252
|
+
Person.connection.add_column "people", "last_name", :string
|
253
|
+
Person.connection.add_column "people", "bio", :text
|
254
|
+
Person.connection.add_column "people", "age", :integer
|
255
|
+
Person.connection.add_column "people", "height", :float
|
256
|
+
Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
|
257
|
+
Person.connection.add_column "people", "birthday", :datetime
|
258
|
+
Person.connection.add_column "people", "favorite_day", :date
|
259
|
+
Person.connection.add_column "people", "male", :boolean
|
260
|
+
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 }
|
261
|
+
bob = Person.find(:first)
|
262
|
+
|
263
|
+
assert_equal 'bob', bob.first_name
|
264
|
+
assert_equal 'bobsen', bob.last_name
|
265
|
+
assert_equal "I was born ....", bob.bio
|
266
|
+
assert_equal 18, bob.age
|
267
|
+
|
268
|
+
# Test for 30 significent digits (beyond the 16 of float), 10 of them
|
269
|
+
# after the decimal place.
|
270
|
+
if current_adapter?(:SQLiteAdapter)
|
271
|
+
# SQLite3 uses float in violation of SQL. Test for 16 decimal places.
|
272
|
+
assert_equal BigDecimal.new('0.123456789012346E20'), bob.wealth
|
273
|
+
else
|
274
|
+
assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
|
275
|
+
end
|
276
|
+
|
277
|
+
assert_equal true, bob.male?
|
278
|
+
|
279
|
+
assert_equal String, bob.first_name.class
|
280
|
+
assert_equal String, bob.last_name.class
|
281
|
+
assert_equal String, bob.bio.class
|
282
|
+
assert_equal Fixnum, bob.age.class
|
283
|
+
assert_equal Time, bob.birthday.class
|
284
|
+
|
285
|
+
if current_adapter?(:SQLServerAdapter, :OracleAdapter, :SybaseAdapter)
|
286
|
+
# Sybase, and Oracle don't differentiate between date/time
|
287
|
+
assert_equal Time, bob.favorite_day.class
|
288
|
+
else
|
289
|
+
assert_equal Date, bob.favorite_day.class
|
290
|
+
end
|
291
|
+
|
292
|
+
assert_equal TrueClass, bob.male?.class
|
293
|
+
assert_kind_of BigDecimal, bob.wealth
|
294
|
+
end
|
295
|
+
|
296
|
+
def test_add_remove_single_field_using_string_arguments
|
297
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
298
|
+
|
299
|
+
ActiveRecord::Migration.add_column 'people', 'last_name', :string
|
300
|
+
|
301
|
+
Person.reset_column_information
|
302
|
+
assert Person.column_methods_hash.include?(:last_name)
|
303
|
+
|
304
|
+
ActiveRecord::Migration.remove_column 'people', 'last_name'
|
305
|
+
|
306
|
+
Person.reset_column_information
|
307
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
308
|
+
end
|
309
|
+
|
310
|
+
def test_add_remove_single_field_using_symbol_arguments
|
311
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
312
|
+
|
313
|
+
ActiveRecord::Migration.add_column :people, :last_name, :string
|
314
|
+
|
315
|
+
Person.reset_column_information
|
316
|
+
assert Person.column_methods_hash.include?(:last_name)
|
317
|
+
|
318
|
+
ActiveRecord::Migration.remove_column :people, :last_name
|
319
|
+
|
320
|
+
Person.reset_column_information
|
321
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
322
|
+
end
|
323
|
+
|
324
|
+
def test_add_rename
|
325
|
+
Person.delete_all
|
326
|
+
|
327
|
+
begin
|
328
|
+
Person.connection.add_column "people", "girlfriend", :string
|
329
|
+
Person.create :girlfriend => 'bobette'
|
330
|
+
|
331
|
+
Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
|
332
|
+
|
333
|
+
Person.reset_column_information
|
334
|
+
bob = Person.find(:first)
|
335
|
+
|
336
|
+
assert_equal "bobette", bob.exgirlfriend
|
337
|
+
ensure
|
338
|
+
Person.connection.remove_column("people", "girlfriend") rescue nil
|
339
|
+
Person.connection.remove_column("people", "exgirlfriend") rescue nil
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def test_rename_column_using_symbol_arguments
|
344
|
+
begin
|
345
|
+
Person.connection.rename_column :people, :first_name, :nick_name
|
346
|
+
Person.reset_column_information
|
347
|
+
assert Person.column_names.include?("nick_name")
|
348
|
+
ensure
|
349
|
+
Person.connection.remove_column("people","nick_name")
|
350
|
+
Person.connection.add_column("people","first_name", :string)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def test_rename_column
|
355
|
+
begin
|
356
|
+
Person.connection.rename_column "people", "first_name", "nick_name"
|
357
|
+
Person.reset_column_information
|
358
|
+
assert Person.column_names.include?("nick_name")
|
359
|
+
ensure
|
360
|
+
Person.connection.remove_column("people","nick_name")
|
361
|
+
Person.connection.add_column("people","first_name", :string)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
def test_rename_table
|
366
|
+
begin
|
367
|
+
ActiveRecord::Base.connection.create_table :octopuses do |t|
|
368
|
+
t.column :url, :string
|
369
|
+
end
|
370
|
+
ActiveRecord::Base.connection.rename_table :octopuses, :octopi
|
371
|
+
|
372
|
+
# Using explicit id in insert for compatibility across all databases
|
373
|
+
con = ActiveRecord::Base.connection
|
374
|
+
con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
|
375
|
+
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')" }
|
376
|
+
con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
|
377
|
+
|
378
|
+
assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
|
379
|
+
|
380
|
+
ensure
|
381
|
+
ActiveRecord::Base.connection.drop_table :octopuses rescue nil
|
382
|
+
ActiveRecord::Base.connection.drop_table :octopi rescue nil
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_rename_table_with_an_index
|
387
|
+
begin
|
388
|
+
ActiveRecord::Base.connection.create_table :octopuses do |t|
|
389
|
+
t.column :url, :string
|
390
|
+
end
|
391
|
+
ActiveRecord::Base.connection.add_index :octopuses, :url
|
392
|
+
|
393
|
+
ActiveRecord::Base.connection.rename_table :octopuses, :octopi
|
394
|
+
|
395
|
+
# Using explicit id in insert for compatibility across all databases
|
396
|
+
con = ActiveRecord::Base.connection
|
397
|
+
con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
|
398
|
+
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')" }
|
399
|
+
con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
|
400
|
+
|
401
|
+
assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
|
402
|
+
assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
|
403
|
+
ensure
|
404
|
+
ActiveRecord::Base.connection.drop_table :octopuses rescue nil
|
405
|
+
ActiveRecord::Base.connection.drop_table :octopi rescue nil
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def test_change_column
|
410
|
+
Person.connection.add_column 'people', 'age', :integer
|
411
|
+
old_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
|
412
|
+
assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
|
413
|
+
|
414
|
+
assert_nothing_raised { Person.connection.change_column "people", "age", :string }
|
415
|
+
|
416
|
+
new_columns = Person.connection.columns(Person.table_name, "#{name} Columns")
|
417
|
+
assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
|
418
|
+
assert new_columns.find { |c| c.name == 'age' and c.type == :string }
|
419
|
+
|
420
|
+
old_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
|
421
|
+
assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
|
422
|
+
assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
|
423
|
+
new_columns = Topic.connection.columns(Topic.table_name, "#{name} Columns")
|
424
|
+
assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
|
425
|
+
assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
|
426
|
+
assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
|
427
|
+
end
|
428
|
+
|
429
|
+
def test_change_column_with_nil_default
|
430
|
+
Person.connection.add_column "people", "contributor", :boolean, :default => true
|
431
|
+
Person.reset_column_information
|
432
|
+
assert Person.new.contributor?
|
433
|
+
|
434
|
+
assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
|
435
|
+
Person.reset_column_information
|
436
|
+
assert !Person.new.contributor?
|
437
|
+
assert_nil Person.new.contributor
|
438
|
+
end
|
439
|
+
|
440
|
+
def test_change_column_with_new_default
|
441
|
+
Person.connection.add_column "people", "administrator", :boolean, :default => true
|
442
|
+
Person.reset_column_information
|
443
|
+
assert Person.new.administrator?
|
444
|
+
|
445
|
+
assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
|
446
|
+
Person.reset_column_information
|
447
|
+
assert !Person.new.administrator?
|
448
|
+
end
|
449
|
+
|
450
|
+
def test_change_column_default
|
451
|
+
Person.connection.change_column_default "people", "first_name", "Tester"
|
452
|
+
Person.reset_column_information
|
453
|
+
assert_equal "Tester", Person.new.first_name
|
454
|
+
end
|
455
|
+
|
456
|
+
def test_change_column_default_to_null
|
457
|
+
Person.connection.change_column_default "people", "first_name", nil
|
458
|
+
Person.reset_column_information
|
459
|
+
assert_nil Person.new.first_name
|
460
|
+
end
|
461
|
+
|
462
|
+
def test_add_table
|
463
|
+
assert !Reminder.table_exists?
|
464
|
+
|
465
|
+
WeNeedReminders.up
|
466
|
+
|
467
|
+
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
|
468
|
+
assert_equal "hello world", Reminder.find(:first).content
|
469
|
+
|
470
|
+
WeNeedReminders.down
|
471
|
+
assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
472
|
+
end
|
473
|
+
|
474
|
+
def test_add_table_with_decimals
|
475
|
+
Person.connection.drop_table :big_numbers rescue nil
|
476
|
+
|
477
|
+
assert !BigNumber.table_exists?
|
478
|
+
GiveMeBigNumbers.up
|
479
|
+
|
480
|
+
assert BigNumber.create(
|
481
|
+
:bank_balance => 1586.43,
|
482
|
+
:big_bank_balance => BigDecimal("1000234000567.95"),
|
483
|
+
:world_population => 6000000000,
|
484
|
+
:my_house_population => 3,
|
485
|
+
:value_of_e => BigDecimal("2.7182818284590452353602875")
|
486
|
+
)
|
487
|
+
|
488
|
+
b = BigNumber.find(:first)
|
489
|
+
assert_not_nil b
|
490
|
+
|
491
|
+
assert_not_nil b.bank_balance
|
492
|
+
assert_not_nil b.big_bank_balance
|
493
|
+
assert_not_nil b.world_population
|
494
|
+
assert_not_nil b.my_house_population
|
495
|
+
assert_not_nil b.value_of_e
|
496
|
+
|
497
|
+
# TODO: set world_population >= 2**62 to cover 64-bit platforms and test
|
498
|
+
# is_a?(Bignum)
|
499
|
+
unless current_adapter?(:IBM_DBAdapter)
|
500
|
+
assert_kind_of Integer, b.world_population
|
501
|
+
else
|
502
|
+
assert_kind_of BigDecimal, b.world_population
|
503
|
+
end
|
504
|
+
assert_equal 6000000000, b.world_population
|
505
|
+
unless current_adapter?(:IBM_DBAdapter)
|
506
|
+
assert_kind_of Fixnum, b.my_house_population
|
507
|
+
else
|
508
|
+
assert_kind_of BigDecimal, b.my_house_population
|
509
|
+
end
|
510
|
+
assert_equal 3, b.my_house_population
|
511
|
+
assert_kind_of BigDecimal, b.bank_balance
|
512
|
+
assert_equal BigDecimal("1586.43"), b.bank_balance
|
513
|
+
assert_kind_of BigDecimal, b.big_bank_balance
|
514
|
+
assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
|
515
|
+
|
516
|
+
# This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
|
517
|
+
# precision/scale explictly left out. By the SQL standard, numbers
|
518
|
+
# assigned to this field should be truncated but that's seldom respected.
|
519
|
+
if current_adapter?(:PostgreSQLAdapter, :SQLite2Adapter)
|
520
|
+
# - PostgreSQL changes the SQL spec on columns declared simply as
|
521
|
+
# "decimal" to something more useful: instead of being given a scale
|
522
|
+
# of 0, they take on the compile-time limit for precision and scale,
|
523
|
+
# so the following should succeed unless you have used really wacky
|
524
|
+
# compilation options
|
525
|
+
# - SQLite2 has the default behavior of preserving all data sent in,
|
526
|
+
# so this happens there too
|
527
|
+
assert_kind_of BigDecimal, b.value_of_e
|
528
|
+
assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
|
529
|
+
elsif current_adapter?(:SQLiteAdapter)
|
530
|
+
# - SQLite3 stores a float, in violation of SQL
|
531
|
+
assert_kind_of BigDecimal, b.value_of_e
|
532
|
+
assert_equal BigDecimal("2.71828182845905"), b.value_of_e
|
533
|
+
elsif current_adapter?(:SQLServer)
|
534
|
+
# - SQL Server rounds instead of truncating
|
535
|
+
assert_kind_of Fixnum, b.value_of_e
|
536
|
+
assert_equal 3, b.value_of_e
|
537
|
+
else
|
538
|
+
# - SQL standard is an integer
|
539
|
+
unless current_adapter?(:IBM_DBAdapter)
|
540
|
+
assert_kind_of Fixnum, b.value_of_e
|
541
|
+
else
|
542
|
+
assert_kind_of BigDecimal, b.value_of_e
|
543
|
+
end
|
544
|
+
assert_equal 2, b.value_of_e
|
545
|
+
end
|
546
|
+
|
547
|
+
GiveMeBigNumbers.down
|
548
|
+
assert_raises(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
|
549
|
+
end
|
550
|
+
|
551
|
+
def test_migrator
|
552
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
553
|
+
assert !Reminder.table_exists?
|
554
|
+
|
555
|
+
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
|
556
|
+
|
557
|
+
assert_equal 3, ActiveRecord::Migrator.current_version
|
558
|
+
Person.reset_column_information
|
559
|
+
assert Person.column_methods_hash.include?(:last_name)
|
560
|
+
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
|
561
|
+
assert_equal "hello world", Reminder.find(:first).content
|
562
|
+
|
563
|
+
ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/')
|
564
|
+
|
565
|
+
assert_equal 0, ActiveRecord::Migrator.current_version
|
566
|
+
Person.reset_column_information
|
567
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
568
|
+
assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
569
|
+
end
|
570
|
+
|
571
|
+
def test_migrator_one_up
|
572
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
573
|
+
assert !Reminder.table_exists?
|
574
|
+
|
575
|
+
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
|
576
|
+
|
577
|
+
Person.reset_column_information
|
578
|
+
assert Person.column_methods_hash.include?(:last_name)
|
579
|
+
assert !Reminder.table_exists?
|
580
|
+
|
581
|
+
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 2)
|
582
|
+
|
583
|
+
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
|
584
|
+
assert_equal "hello world", Reminder.find(:first).content
|
585
|
+
end
|
586
|
+
|
587
|
+
def test_migrator_one_down
|
588
|
+
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/')
|
589
|
+
|
590
|
+
ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
|
591
|
+
|
592
|
+
Person.reset_column_information
|
593
|
+
assert Person.column_methods_hash.include?(:last_name)
|
594
|
+
assert !Reminder.table_exists?
|
595
|
+
end
|
596
|
+
|
597
|
+
def test_migrator_one_up_one_down
|
598
|
+
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
|
599
|
+
ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
|
600
|
+
|
601
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
602
|
+
assert !Reminder.table_exists?
|
603
|
+
end
|
604
|
+
|
605
|
+
def test_migrator_verbosity
|
606
|
+
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
|
607
|
+
assert PeopleHaveLastNames.message_count > 0
|
608
|
+
PeopleHaveLastNames.message_count = 0
|
609
|
+
|
610
|
+
ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
|
611
|
+
assert PeopleHaveLastNames.message_count > 0
|
612
|
+
PeopleHaveLastNames.message_count = 0
|
613
|
+
end
|
614
|
+
|
615
|
+
def test_migrator_verbosity_off
|
616
|
+
PeopleHaveLastNames.verbose = false
|
617
|
+
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
|
618
|
+
assert PeopleHaveLastNames.message_count.zero?
|
619
|
+
ActiveRecord::Migrator.down(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
|
620
|
+
assert PeopleHaveLastNames.message_count.zero?
|
621
|
+
end
|
622
|
+
|
623
|
+
def test_migrator_going_down_due_to_version_target
|
624
|
+
ActiveRecord::Migrator.up(File.dirname(__FILE__) + '/fixtures/migrations/', 1)
|
625
|
+
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/', 0)
|
626
|
+
|
627
|
+
assert !Person.column_methods_hash.include?(:last_name)
|
628
|
+
assert !Reminder.table_exists?
|
629
|
+
|
630
|
+
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations/')
|
631
|
+
|
632
|
+
Person.reset_column_information
|
633
|
+
assert Person.column_methods_hash.include?(:last_name)
|
634
|
+
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
|
635
|
+
assert_equal "hello world", Reminder.find(:first).content
|
636
|
+
end
|
637
|
+
|
638
|
+
def test_schema_info_table_name
|
639
|
+
ActiveRecord::Base.table_name_prefix = "prefix_"
|
640
|
+
ActiveRecord::Base.table_name_suffix = "_suffix"
|
641
|
+
Reminder.reset_table_name
|
642
|
+
assert_equal "prefix_schema_info_suffix", ActiveRecord::Migrator.schema_info_table_name
|
643
|
+
ActiveRecord::Base.table_name_prefix = ""
|
644
|
+
ActiveRecord::Base.table_name_suffix = ""
|
645
|
+
Reminder.reset_table_name
|
646
|
+
assert_equal "schema_info", ActiveRecord::Migrator.schema_info_table_name
|
647
|
+
ensure
|
648
|
+
ActiveRecord::Base.table_name_prefix = ""
|
649
|
+
ActiveRecord::Base.table_name_suffix = ""
|
650
|
+
end
|
651
|
+
|
652
|
+
def test_proper_table_name
|
653
|
+
assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
|
654
|
+
assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
|
655
|
+
assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
|
656
|
+
Reminder.reset_table_name
|
657
|
+
assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
|
658
|
+
|
659
|
+
# Use the model's own prefix/suffix if a model is given
|
660
|
+
ActiveRecord::Base.table_name_prefix = "ARprefix_"
|
661
|
+
ActiveRecord::Base.table_name_suffix = "_ARsuffix"
|
662
|
+
Reminder.table_name_prefix = 'prefix_'
|
663
|
+
Reminder.table_name_suffix = '_suffix'
|
664
|
+
Reminder.reset_table_name
|
665
|
+
assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
|
666
|
+
Reminder.table_name_prefix = ''
|
667
|
+
Reminder.table_name_suffix = ''
|
668
|
+
Reminder.reset_table_name
|
669
|
+
|
670
|
+
# Use AR::Base's prefix/suffix if string or symbol is given
|
671
|
+
ActiveRecord::Base.table_name_prefix = "prefix_"
|
672
|
+
ActiveRecord::Base.table_name_suffix = "_suffix"
|
673
|
+
Reminder.reset_table_name
|
674
|
+
assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
|
675
|
+
assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
|
676
|
+
ActiveRecord::Base.table_name_prefix = ""
|
677
|
+
ActiveRecord::Base.table_name_suffix = ""
|
678
|
+
Reminder.reset_table_name
|
679
|
+
end
|
680
|
+
|
681
|
+
def test_add_drop_table_with_prefix_and_suffix
|
682
|
+
assert !Reminder.table_exists?
|
683
|
+
ActiveRecord::Base.table_name_prefix = 'prefix_'
|
684
|
+
ActiveRecord::Base.table_name_suffix = '_suffix'
|
685
|
+
Reminder.reset_table_name
|
686
|
+
Reminder.reset_sequence_name
|
687
|
+
WeNeedReminders.up
|
688
|
+
assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
|
689
|
+
assert_equal "hello world", Reminder.find(:first).content
|
690
|
+
|
691
|
+
WeNeedReminders.down
|
692
|
+
assert_raises(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
|
693
|
+
ensure
|
694
|
+
ActiveRecord::Base.table_name_prefix = ''
|
695
|
+
ActiveRecord::Base.table_name_suffix = ''
|
696
|
+
Reminder.reset_table_name
|
697
|
+
Reminder.reset_sequence_name
|
698
|
+
end
|
699
|
+
|
700
|
+
# FrontBase does not support default values on BLOB/CLOB columns
|
701
|
+
unless current_adapter?(:FrontBaseAdapter)
|
702
|
+
def test_create_table_with_binary_column
|
703
|
+
Person.connection.drop_table :binary_testings rescue nil
|
704
|
+
|
705
|
+
assert_nothing_raised {
|
706
|
+
Person.connection.create_table :binary_testings do |t|
|
707
|
+
t.column "data", :binary, :default => "", :null => false
|
708
|
+
end
|
709
|
+
}
|
710
|
+
|
711
|
+
columns = Person.connection.columns(:binary_testings)
|
712
|
+
data_column = columns.detect { |c| c.name == "data" }
|
713
|
+
|
714
|
+
if current_adapter?(:OracleAdapter)
|
715
|
+
assert_equal "empty_blob()", data_column.default
|
716
|
+
else
|
717
|
+
assert_equal "", data_column.default
|
718
|
+
end
|
719
|
+
|
720
|
+
Person.connection.drop_table :binary_testings rescue nil
|
721
|
+
end
|
722
|
+
end
|
723
|
+
def test_migrator_with_duplicates
|
724
|
+
assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
|
725
|
+
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_duplicate/', nil)
|
726
|
+
end
|
727
|
+
end
|
728
|
+
|
729
|
+
def test_migrator_with_missing_version_numbers
|
730
|
+
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 500)
|
731
|
+
assert !Person.column_methods_hash.include?(:middle_name)
|
732
|
+
assert_equal 4, ActiveRecord::Migrator.current_version
|
733
|
+
|
734
|
+
ActiveRecord::Migrator.migrate(File.dirname(__FILE__) + '/fixtures/migrations_with_missing_versions/', 2)
|
735
|
+
assert !Reminder.table_exists?
|
736
|
+
assert Person.column_methods_hash.include?(:last_name)
|
737
|
+
assert_equal 2, ActiveRecord::Migrator.current_version
|
738
|
+
end
|
739
|
+
|
740
|
+
def test_create_table_with_custom_sequence_name
|
741
|
+
return unless current_adapter? :OracleAdapter
|
742
|
+
|
743
|
+
# table name is 29 chars, the standard sequence name will
|
744
|
+
# be 33 chars and fail
|
745
|
+
assert_raises(ActiveRecord::StatementInvalid) do
|
746
|
+
begin
|
747
|
+
Person.connection.create_table :table_with_name_thats_just_ok do |t|
|
748
|
+
t.column :foo, :string, :null => false
|
749
|
+
end
|
750
|
+
ensure
|
751
|
+
Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
# should be all good w/ a custom sequence name
|
756
|
+
assert_nothing_raised do
|
757
|
+
begin
|
758
|
+
Person.connection.create_table :table_with_name_thats_just_ok,
|
759
|
+
:sequence_name => 'suitably_short_seq' do |t|
|
760
|
+
t.column :foo, :string, :null => false
|
761
|
+
end
|
762
|
+
|
763
|
+
Person.connection.execute("select suitably_short_seq.nextval from dual")
|
764
|
+
|
765
|
+
ensure
|
766
|
+
Person.connection.drop_table :table_with_name_thats_just_ok,
|
767
|
+
:sequence_name => 'suitably_short_seq' rescue nil
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
# confirm the custom sequence got dropped
|
772
|
+
assert_raises(ActiveRecord::StatementInvalid) do
|
773
|
+
Person.connection.execute("select suitably_short_seq.nextval from dual")
|
774
|
+
end
|
775
|
+
end
|
776
|
+
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|