pmacs-activerecord-oracle_enhanced-adapter 1.4.2.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.rspec +2 -0
  2. data/Gemfile +52 -0
  3. data/History.md +284 -0
  4. data/License.txt +20 -0
  5. data/README.md +403 -0
  6. data/RUNNING_TESTS.md +45 -0
  7. data/Rakefile +59 -0
  8. data/VERSION +1 -0
  9. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced.rake +105 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +41 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1408 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +118 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +141 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +135 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +359 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +25 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +44 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +565 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +491 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +260 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +231 -0
  24. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +257 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +397 -0
  26. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +265 -0
  27. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +294 -0
  28. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +17 -0
  29. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
  30. data/lib/pmacs-activerecord-oracle_enhanced-adapter.rb +25 -0
  31. data/pmacs-activerecord-oracle_enhanced-adapter.gemspec +131 -0
  32. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +778 -0
  33. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +332 -0
  34. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +427 -0
  35. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +19 -0
  36. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +113 -0
  37. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +1376 -0
  38. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +69 -0
  39. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +141 -0
  40. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +25 -0
  41. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +378 -0
  42. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +438 -0
  43. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +1280 -0
  44. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +339 -0
  45. data/spec/spec_helper.rb +187 -0
  46. metadata +302 -0
@@ -0,0 +1,438 @@
1
+ require 'spec_helper'
2
+
3
+ describe "OracleEnhancedAdapter schema dump" do
4
+ include SchemaSpecHelper
5
+
6
+ before(:all) do
7
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
8
+ @conn = ActiveRecord::Base.connection
9
+ @oracle11g = !! @conn.select_value("SELECT * FROM v$version WHERE banner LIKE 'Oracle%11g%'")
10
+ end
11
+
12
+ def standard_dump(options = {})
13
+ stream = StringIO.new
14
+ ActiveRecord::SchemaDumper.ignore_tables = options[:ignore_tables]||[]
15
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
16
+ stream.string
17
+ end
18
+
19
+ def create_test_posts_table(options = {})
20
+ options.merge! :force => true
21
+ schema_define do
22
+ create_table :test_posts, options do |t|
23
+ t.string :title
24
+ t.timestamps
25
+ end
26
+ add_index :test_posts, :title
27
+ end
28
+ end
29
+
30
+ def drop_test_posts_table
31
+ schema_define do
32
+ drop_table :test_posts
33
+ end
34
+ rescue
35
+ nil
36
+ end
37
+
38
+ describe "tables" do
39
+ after(:each) do
40
+ drop_test_posts_table
41
+ end
42
+
43
+ it "should not include ignored table names in schema dump" do
44
+ create_test_posts_table
45
+ standard_dump(:ignore_tables => %w(test_posts)).should_not =~ /create_table "test_posts"/
46
+ end
47
+
48
+ it "should not include ignored table regexes in schema dump" do
49
+ create_test_posts_table
50
+ standard_dump(:ignore_tables => [ /test_posts/i ]).should_not =~ /create_table "test_posts"/
51
+ end
52
+
53
+ end
54
+
55
+ describe "dumping default values" do
56
+ before :each do
57
+ schema_define do
58
+ create_table "test_defaults", :force => true do |t|
59
+ t.string "regular", :default => "c"
60
+ t.string "special_c", :default => "\n"
61
+ end
62
+ end
63
+ end
64
+
65
+ after(:each) do
66
+ schema_define do
67
+ drop_table "test_defaults"
68
+ end
69
+ end
70
+
71
+ it "should be able to dump default values using special characters" do
72
+ standard_dump.should =~ /t.string \"special_c\", :default => "\\n"/
73
+ end
74
+ end
75
+ describe "table prefixes and suffixes" do
76
+ after(:each) do
77
+ drop_test_posts_table
78
+ @conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if @conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
79
+ ActiveRecord::Base.table_name_prefix = ''
80
+ ActiveRecord::Base.table_name_suffix = ''
81
+ end
82
+
83
+ it "should remove table prefix in schema dump" do
84
+ ActiveRecord::Base.table_name_prefix = 'xxx_'
85
+ create_test_posts_table
86
+ standard_dump.should =~ /create_table "test_posts".*add_index "test_posts"/m
87
+ end
88
+
89
+ it "should remove table prefix with $ sign in schema dump" do
90
+ ActiveRecord::Base.table_name_prefix = 'xxx$'
91
+ create_test_posts_table
92
+ standard_dump.should =~ /create_table "test_posts".*add_index "test_posts"/m
93
+ end
94
+
95
+ it "should remove table suffix in schema dump" do
96
+ ActiveRecord::Base.table_name_suffix = '_xxx'
97
+ create_test_posts_table
98
+ standard_dump.should =~ /create_table "test_posts".*add_index "test_posts"/m
99
+ end
100
+
101
+ it "should remove table suffix with $ sign in schema dump" do
102
+ ActiveRecord::Base.table_name_suffix = '$xxx'
103
+ create_test_posts_table
104
+ standard_dump.should =~ /create_table "test_posts".*add_index "test_posts"/m
105
+ end
106
+
107
+ it "should not include schema_migrations table with prefix in schema dump" do
108
+ ActiveRecord::Base.table_name_prefix = 'xxx_'
109
+ @conn.initialize_schema_migrations_table
110
+ standard_dump.should_not =~ /schema_migrations/
111
+ end
112
+
113
+ it "should not include schema_migrations table with suffix in schema dump" do
114
+ ActiveRecord::Base.table_name_suffix = '_xxx'
115
+ @conn.initialize_schema_migrations_table
116
+ standard_dump.should_not =~ /schema_migrations/
117
+ end
118
+
119
+ end
120
+
121
+ describe "table with non-default primary key" do
122
+ after(:each) do
123
+ drop_test_posts_table
124
+ end
125
+
126
+ it "should include non-default primary key in schema dump" do
127
+ create_test_posts_table(:primary_key => 'post_id')
128
+ standard_dump.should =~ /create_table "test_posts", :primary_key => "post_id"/
129
+ end
130
+
131
+ end
132
+
133
+ describe "table with primary key trigger" do
134
+
135
+ after(:each) do
136
+ drop_test_posts_table
137
+ @conn.clear_prefetch_primary_key
138
+ end
139
+
140
+ it "should include primary key trigger in schema dump" do
141
+ create_test_posts_table(:primary_key_trigger => true)
142
+ standard_dump.should =~ /create_table "test_posts".*add_primary_key_trigger "test_posts"/m
143
+ end
144
+
145
+ it "should include primary key trigger with non-default primary key in schema dump" do
146
+ create_test_posts_table(:primary_key_trigger => true, :primary_key => 'post_id')
147
+ standard_dump.should =~ /create_table "test_posts", :primary_key => "post_id".*add_primary_key_trigger "test_posts", :primary_key => "post_id"/m
148
+ end
149
+
150
+ end
151
+
152
+ describe "foreign key constraints" do
153
+ before(:all) do
154
+ schema_define do
155
+ create_table :test_posts, :force => true do |t|
156
+ t.string :title
157
+ end
158
+ create_table :test_comments, :force => true do |t|
159
+ t.string :body, :limit => 4000
160
+ t.references :test_post
161
+ end
162
+ end
163
+ end
164
+
165
+ after(:each) do
166
+ schema_define do
167
+ remove_foreign_key :test_comments, :test_posts rescue nil
168
+ remove_foreign_key :test_comments, :name => 'comments_posts_baz_fooz_fk' rescue nil
169
+ end
170
+ end
171
+ after(:all) do
172
+ schema_define do
173
+ drop_table :test_comments rescue nil
174
+ drop_table :test_posts rescue nil
175
+ end
176
+ end
177
+
178
+ it "should include foreign key in schema dump" do
179
+ schema_define do
180
+ add_foreign_key :test_comments, :test_posts
181
+ end
182
+ standard_dump.should =~ /add_foreign_key "test_comments", "test_posts", :name => "test_comments_test_post_id_fk"/
183
+ end
184
+
185
+ it "should include foreign key with delete dependency in schema dump" do
186
+ schema_define do
187
+ add_foreign_key :test_comments, :test_posts, :dependent => :delete
188
+ end
189
+ standard_dump.should =~ /add_foreign_key "test_comments", "test_posts", :name => "test_comments_test_post_id_fk", :dependent => :delete/
190
+ end
191
+
192
+ it "should include foreign key with nullify dependency in schema dump" do
193
+ schema_define do
194
+ add_foreign_key :test_comments, :test_posts, :dependent => :nullify
195
+ end
196
+ standard_dump.should =~ /add_foreign_key "test_comments", "test_posts", :name => "test_comments_test_post_id_fk", :dependent => :nullify/
197
+ end
198
+
199
+ it "should not include foreign keys on ignored table names in schema dump" do
200
+ schema_define do
201
+ add_foreign_key :test_comments, :test_posts
202
+ end
203
+ standard_dump(:ignore_tables => %w(test_comments)).should_not =~ /add_foreign_key "test_comments"/
204
+ end
205
+
206
+ it "should not include foreign keys on ignored table regexes in schema dump" do
207
+ schema_define do
208
+ add_foreign_key :test_comments, :test_posts
209
+ end
210
+ standard_dump(:ignore_tables => [ /test_comments/i ]).should_not =~ /add_foreign_key "test_comments"/
211
+ end
212
+
213
+ it "should include foreign keys referencing ignored table names in schema dump" do
214
+ schema_define do
215
+ add_foreign_key :test_comments, :test_posts
216
+ end
217
+ standard_dump(:ignore_tables => %w(test_posts)).should =~ /add_foreign_key "test_comments"/
218
+ end
219
+
220
+ it "should include foreign keys referencing ignored table regexes in schema dump" do
221
+ schema_define do
222
+ add_foreign_key :test_comments, :test_posts
223
+ end
224
+ standard_dump(:ignore_tables => [ /test_posts/i ]).should =~ /add_foreign_key "test_comments"/
225
+ end
226
+
227
+ it "should include composite foreign keys" do
228
+ schema_define do
229
+ add_column :test_posts, :baz_id, :integer
230
+ add_column :test_posts, :fooz_id, :integer
231
+
232
+ execute <<-SQL
233
+ ALTER TABLE TEST_POSTS
234
+ ADD CONSTRAINT UK_FOOZ_BAZ UNIQUE (BAZ_ID,FOOZ_ID)
235
+ SQL
236
+
237
+ add_column :test_comments, :baz_id, :integer
238
+ add_column :test_comments, :fooz_id, :integer
239
+
240
+ add_foreign_key :test_comments, :test_posts, :columns => ["baz_id", "fooz_id"], :name => 'comments_posts_baz_fooz_fk'
241
+ end
242
+ standard_dump.should =~ /add_foreign_key "test_comments", "test_posts", :columns => \["baz_id", "fooz_id"\], :name => "comments_posts_baz_fooz_fk"/
243
+ end
244
+ it "should include foreign keys following all tables" do
245
+ # if foreign keys preceed declaration of all tables
246
+ # it can cause problems when using db:test rake tasks
247
+ schema_define do
248
+ add_foreign_key :test_comments, :test_posts
249
+ end
250
+ dump = standard_dump
251
+ dump.rindex("create_table").should < dump.index("add_foreign_key")
252
+ end
253
+ end
254
+
255
+ describe "synonyms" do
256
+ after(:each) do
257
+ schema_define do
258
+ remove_synonym :test_synonym
259
+ end
260
+ end
261
+
262
+ it "should include synonym to other schema table in schema dump" do
263
+ schema_define do
264
+ add_synonym :test_synonym, "schema_name.table_name", :force => true
265
+ end
266
+ standard_dump.should =~ /add_synonym "test_synonym", "schema_name.table_name", :force => true/
267
+ end
268
+
269
+ it "should include synonym to other database table in schema dump" do
270
+ schema_define do
271
+ add_synonym :test_synonym, "table_name@link_name", :force => true
272
+ end
273
+ standard_dump.should =~ /add_synonym "test_synonym", "table_name@link_name(\.[-A-Za-z0-9_]+)*", :force => true/
274
+ end
275
+
276
+ it "should not include ignored table names in schema dump" do
277
+ schema_define do
278
+ add_synonym :test_synonym, "schema_name.table_name", :force => true
279
+ end
280
+ standard_dump(:ignore_tables => %w(test_synonym)).should_not =~ /add_synonym "test_synonym"/
281
+ end
282
+
283
+ it "should not include ignored table regexes in schema dump" do
284
+ schema_define do
285
+ add_synonym :test_synonym, "schema_name.table_name", :force => true
286
+ end
287
+ standard_dump(:ignore_tables => [ /test_synonym/i ]).should_not =~ /add_synonym "test_synonym"/
288
+ end
289
+
290
+ it "should include synonyms to ignored table regexes in schema dump" do
291
+ schema_define do
292
+ add_synonym :test_synonym, "schema_name.table_name", :force => true
293
+ end
294
+ standard_dump(:ignore_tables => [ /table_name/i ]).should =~ /add_synonym "test_synonym"/
295
+ end
296
+
297
+ end
298
+
299
+ describe "temporary tables" do
300
+ after(:each) do
301
+ drop_test_posts_table
302
+ end
303
+
304
+ it "should include temporary options" do
305
+ create_test_posts_table(:temporary => true)
306
+ standard_dump.should =~ /create_table "test_posts", :temporary => true/
307
+ end
308
+ end
309
+
310
+ describe "indexes" do
311
+ after(:each) do
312
+ drop_test_posts_table
313
+ end
314
+
315
+ it "should not specify default tablespace in add index" do
316
+ create_test_posts_table
317
+ standard_dump.should =~ /add_index "test_posts", \["title"\], :name => "index_test_posts_on_title"$/
318
+ end
319
+
320
+ it "should specify non-default tablespace in add index" do
321
+ tablespace_name = @conn.default_tablespace
322
+ @conn.stub!(:default_tablespace).and_return('dummy')
323
+ create_test_posts_table
324
+ standard_dump.should =~ /add_index "test_posts", \["title"\], :name => "index_test_posts_on_title", :tablespace => "#{tablespace_name}"$/
325
+ end
326
+
327
+ it "should create and dump function-based indexes" do
328
+ create_test_posts_table
329
+ @conn.add_index :test_posts, "NVL(created_at, updated_at)", :name => "index_test_posts_cr_upd_at"
330
+ standard_dump.should =~ /add_index "test_posts", \["NVL\(\\"CREATED_AT\\",\\"UPDATED_AT\\"\)"\], :name => "index_test_posts_cr_upd_at"$/
331
+ end
332
+
333
+ end
334
+
335
+ describe "materialized views" do
336
+ after(:each) do
337
+ @conn.execute "DROP MATERIALIZED VIEW test_posts_mv" rescue nil
338
+ drop_test_posts_table
339
+ end
340
+
341
+ it "should not include materialized views in schema dump" do
342
+ create_test_posts_table
343
+ @conn.execute "CREATE MATERIALIZED VIEW test_posts_mv AS SELECT * FROM test_posts"
344
+ standard_dump.should_not =~ /create_table "test_posts_mv"/
345
+ end
346
+ end
347
+
348
+ describe 'virtual columns' do
349
+ before(:all) do
350
+ if @oracle11g
351
+ schema_define do
352
+ create_table :test_names, :force => true do |t|
353
+ t.string :first_name
354
+ t.string :last_name
355
+ t.virtual :full_name, :as => "first_name || ', ' || last_name"
356
+ t.virtual :short_name, :as => "COALESCE(first_name, last_name)", :type => :string, :limit => 300
357
+ t.virtual :abbrev_name, :as => "SUBSTR(first_name,1,50) || ' ' || SUBSTR(last_name,1,1) || '.'", :type => "VARCHAR(100)"
358
+ t.column :full_name_length, :virtual, :as => "length(first_name || ', ' || last_name)", :type => :integer
359
+ t.virtual :field_with_leading_space, :as => "' ' || first_name || ' '", :limit => 300, :type => :string
360
+ end
361
+ end
362
+ else
363
+ pending "Not supported in this database version"
364
+ end
365
+ end
366
+
367
+ before(:each) do
368
+ if @oracle11g
369
+ class ::TestName < ActiveRecord::Base
370
+ if self.respond_to?(:table_name=)
371
+ self.table_name = "test_names"
372
+ else
373
+ set_table_name "test_names"
374
+ end
375
+ end
376
+ end
377
+ end
378
+
379
+ after(:all) do
380
+ if @oracle11g
381
+ schema_define do
382
+ drop_table :test_names
383
+ end
384
+ end
385
+ end
386
+
387
+ it 'should dump correctly' do
388
+ standard_dump.should =~ /t\.virtual "full_name",(\s*):limit => 512,(\s*):as => "\\"FIRST_NAME\\"\|\|', '\|\|\\"LAST_NAME\\"",(\s*):type => :string/
389
+ standard_dump.should =~ /t\.virtual "short_name",(\s*):limit => 300,(\s*):as =>(.*),(\s*):type => :string/
390
+ standard_dump.should =~ /t\.virtual "full_name_length",(\s*):precision => 38,(\s*):scale => 0,(\s*):as =>(.*),(\s*):type => :integer/
391
+ standard_dump.should =~ /t\.virtual "abbrev_name",(\s*):limit => 100,(\s*):as =>(.*),(\s*):type => :string/
392
+ standard_dump.should =~ /t\.virtual "field_with_leading_space",(\s*):limit => 300,(\s*):as => "' '\|\|\\"FIRST_NAME\\"\|\|' '",(\s*):type => :string/
393
+ end
394
+
395
+ context 'with column cache' do
396
+ before(:all) do
397
+ @old_cache = ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns
398
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = true
399
+ end
400
+ after(:all) do
401
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = @old_cache
402
+ end
403
+ it 'should not change column defaults after several dumps' do
404
+ col = TestName.columns.detect{|c| c.name == 'full_name'}
405
+ col.should_not be_nil
406
+ col.virtual_column_data_default.should_not =~ /:as/
407
+
408
+ standard_dump
409
+ col.virtual_column_data_default.should_not =~ /:as/
410
+
411
+ standard_dump
412
+ col.virtual_column_data_default.should_not =~ /:as/
413
+ end
414
+ end
415
+
416
+ context "with index on virtual column" do
417
+ before(:all) do
418
+ if @oracle11g
419
+ schema_define do
420
+ add_index 'test_names', 'field_with_leading_space', :name => "index_on_virtual_col"
421
+ end
422
+ end
423
+ end
424
+ after(:all) do
425
+ if @oracle11g
426
+ schema_define do
427
+ remove_index 'test_names', :name => 'index_on_virtual_col'
428
+ end
429
+ end
430
+ end
431
+ it 'should dump correctly' do
432
+ standard_dump.should_not =~ /add_index "test_names".+FIRST_NAME.+$/
433
+ standard_dump.should =~ /add_index "test_names".+field_with_leading_space.+$/
434
+ end
435
+ end
436
+ end
437
+
438
+ end
@@ -0,0 +1,1280 @@
1
+ require 'spec_helper'
2
+
3
+ describe "OracleEnhancedAdapter schema definition" do
4
+ include SchemaSpecHelper
5
+
6
+ before(:all) do
7
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
8
+ @oracle11g = !! ActiveRecord::Base.connection.select_value("SELECT * FROM v$version WHERE banner LIKE 'Oracle%11g%'")
9
+ end
10
+
11
+ describe "table and sequence creation with non-default primary key" do
12
+
13
+ before(:all) do
14
+ @conn = ActiveRecord::Base.connection
15
+ schema_define do
16
+ create_table :keyboards, :force => true, :id => false do |t|
17
+ t.primary_key :key_number
18
+ t.string :name
19
+ end
20
+ create_table :id_keyboards, :force => true do |t|
21
+ t.string :name
22
+ end
23
+ end
24
+ class ::Keyboard < ActiveRecord::Base
25
+ if self.respond_to?(:primary_key=)
26
+ self.primary_key = :key_number
27
+ else
28
+ set_primary_key :key_number
29
+ end
30
+ end
31
+ class ::IdKeyboard < ActiveRecord::Base
32
+ end
33
+ end
34
+
35
+ after(:all) do
36
+ schema_define do
37
+ drop_table :keyboards
38
+ drop_table :id_keyboards
39
+ end
40
+ Object.send(:remove_const, "Keyboard")
41
+ Object.send(:remove_const, "IdKeyboard")
42
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
43
+ end
44
+
45
+ it "should create sequence for non-default primary key" do
46
+ ActiveRecord::Base.connection.next_sequence_value(Keyboard.sequence_name).should_not be_nil
47
+ end
48
+
49
+ it "should create sequence for default primary key" do
50
+ ActiveRecord::Base.connection.next_sequence_value(IdKeyboard.sequence_name).should_not be_nil
51
+ end
52
+ end
53
+
54
+ describe "sequence creation parameters" do
55
+
56
+ def create_test_employees_table(sequence_start_value = nil)
57
+ schema_define do
58
+ create_table :test_employees, sequence_start_value ? {:sequence_start_value => sequence_start_value} : {} do |t|
59
+ t.string :first_name
60
+ t.string :last_name
61
+ end
62
+ end
63
+ end
64
+
65
+ def save_default_sequence_start_value
66
+ @saved_sequence_start_value = ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value
67
+ end
68
+
69
+ def restore_default_sequence_start_value
70
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value = @saved_sequence_start_value
71
+ end
72
+
73
+ before(:all) do
74
+ @conn = ActiveRecord::Base.connection
75
+ end
76
+
77
+ before(:each) do
78
+ save_default_sequence_start_value
79
+ end
80
+
81
+ after(:each) do
82
+ restore_default_sequence_start_value
83
+ schema_define do
84
+ drop_table :test_employees
85
+ end
86
+ Object.send(:remove_const, "TestEmployee")
87
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
88
+ end
89
+
90
+ it "should use default sequence start value 10000" do
91
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value.should == 10000
92
+
93
+ create_test_employees_table
94
+ class ::TestEmployee < ActiveRecord::Base; end
95
+
96
+ employee = TestEmployee.create!
97
+ employee.id.should == 10000
98
+ end
99
+
100
+ it "should use specified default sequence start value" do
101
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value = 1
102
+
103
+ create_test_employees_table
104
+ class ::TestEmployee < ActiveRecord::Base; end
105
+
106
+ employee = TestEmployee.create!
107
+ employee.id.should == 1
108
+ end
109
+
110
+ it "should use sequence start value from table definition" do
111
+ create_test_employees_table(10)
112
+ class ::TestEmployee < ActiveRecord::Base; end
113
+
114
+ employee = TestEmployee.create!
115
+ employee.id.should == 10
116
+ end
117
+
118
+ it "should use sequence start value and other options from table definition" do
119
+ create_test_employees_table("100 NOCACHE INCREMENT BY 10")
120
+ class ::TestEmployee < ActiveRecord::Base; end
121
+
122
+ employee = TestEmployee.create!
123
+ employee.id.should == 100
124
+ employee = TestEmployee.create!
125
+ employee.id.should == 110
126
+ end
127
+
128
+ end
129
+
130
+ describe "create table with primary key trigger" do
131
+ def create_table_with_trigger(options = {})
132
+ options.merge! :primary_key_trigger => true, :force => true
133
+ schema_define do
134
+ create_table :test_employees, options do |t|
135
+ t.string :first_name
136
+ t.string :last_name
137
+ end
138
+ end
139
+ end
140
+
141
+ def create_table_and_separately_trigger(options = {})
142
+ options.merge! :force => true
143
+ schema_define do
144
+ create_table :test_employees, options do |t|
145
+ t.string :first_name
146
+ t.string :last_name
147
+ end
148
+ add_primary_key_trigger :test_employees, options
149
+ end
150
+ end
151
+
152
+ def drop_table_with_trigger(options = {})
153
+ seq_name = options[:sequence_name]
154
+ schema_define do
155
+ drop_table :test_employees, (seq_name ? {:sequence_name => seq_name} : {})
156
+ end
157
+ Object.send(:remove_const, "TestEmployee")
158
+ @conn.clear_prefetch_primary_key
159
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
160
+ end
161
+
162
+ describe "with default primary key" do
163
+ before(:all) do
164
+ @conn = ActiveRecord::Base.connection
165
+ create_table_with_trigger
166
+ class ::TestEmployee < ActiveRecord::Base
167
+ end
168
+ end
169
+
170
+ after(:all) do
171
+ drop_table_with_trigger
172
+ end
173
+
174
+ it "should populate primary key using trigger" do
175
+ lambda do
176
+ @conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
177
+ end.should_not raise_error
178
+ end
179
+
180
+ it "should return new key value using connection insert method" do
181
+ insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, "id")
182
+ @conn.select_value("SELECT test_employees_seq.currval FROM dual").should == insert_id
183
+ end
184
+
185
+ it "should create new record for model" do
186
+ e = TestEmployee.create!(:first_name => 'Raimonds')
187
+ @conn.select_value("SELECT test_employees_seq.currval FROM dual").should == e.id
188
+ end
189
+ end
190
+
191
+ describe "with separate creation of primary key trigger" do
192
+ before(:all) do
193
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
194
+ @conn = ActiveRecord::Base.connection
195
+ create_table_and_separately_trigger
196
+ class ::TestEmployee < ActiveRecord::Base
197
+ end
198
+ end
199
+
200
+ after(:all) do
201
+ drop_table_with_trigger
202
+ end
203
+
204
+ it "should populate primary key using trigger" do
205
+ lambda do
206
+ @conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
207
+ end.should_not raise_error
208
+ end
209
+
210
+ it "should return new key value using connection insert method" do
211
+ insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, "id")
212
+ @conn.select_value("SELECT test_employees_seq.currval FROM dual").should == insert_id
213
+ end
214
+
215
+ it "should create new record for model" do
216
+ e = TestEmployee.create!(:first_name => 'Raimonds')
217
+ @conn.select_value("SELECT test_employees_seq.currval FROM dual").should == e.id
218
+ end
219
+ end
220
+
221
+ describe "with non-default primary key and non-default sequence name" do
222
+ before(:all) do
223
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
224
+ @conn = ActiveRecord::Base.connection
225
+ @primary_key = "employee_id"
226
+ @sequence_name = "test_employees_s"
227
+ create_table_with_trigger(:primary_key => @primary_key, :sequence_name => @sequence_name)
228
+ class ::TestEmployee < ActiveRecord::Base
229
+ if self.respond_to?(:primary_key=)
230
+ self.primary_key = "employee_id"
231
+ else
232
+ set_primary_key "employee_id"
233
+ end
234
+ end
235
+ end
236
+
237
+ after(:all) do
238
+ drop_table_with_trigger(:sequence_name => @sequence_name)
239
+ end
240
+
241
+ it "should populate primary key using trigger" do
242
+ lambda do
243
+ @conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
244
+ end.should_not raise_error
245
+ end
246
+
247
+ it "should return new key value using connection insert method" do
248
+ insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, @primary_key)
249
+ @conn.select_value("SELECT #{@sequence_name}.currval FROM dual").should == insert_id
250
+ end
251
+
252
+ it "should create new record for model with autogenerated sequence option" do
253
+ e = TestEmployee.create!(:first_name => 'Raimonds')
254
+ @conn.select_value("SELECT #{@sequence_name}.currval FROM dual").should == e.id
255
+ end
256
+ end
257
+
258
+ describe "with non-default sequence name and non-default trigger name" do
259
+ before(:all) do
260
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
261
+ @conn = ActiveRecord::Base.connection
262
+ @sequence_name = "test_employees_s"
263
+ create_table_with_trigger(:sequence_name => @sequence_name, :trigger_name => "test_employees_t1")
264
+ class ::TestEmployee < ActiveRecord::Base
265
+ if self.respond_to?(:sequence_name=)
266
+ self.sequence_name = :autogenerated
267
+ else
268
+ set_sequence_name :autogenerated
269
+ end
270
+ end
271
+ end
272
+
273
+ after(:all) do
274
+ drop_table_with_trigger(:sequence_name => @sequence_name)
275
+ end
276
+
277
+ it "should populate primary key using trigger" do
278
+ lambda do
279
+ @conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
280
+ end.should_not raise_error
281
+ end
282
+
283
+ it "should return new key value using connection insert method" do
284
+ insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, "id")
285
+ @conn.select_value("SELECT #{@sequence_name}.currval FROM dual").should == insert_id
286
+ end
287
+
288
+ it "should create new record for model with autogenerated sequence option" do
289
+ e = TestEmployee.create!(:first_name => 'Raimonds')
290
+ @conn.select_value("SELECT #{@sequence_name}.currval FROM dual").should == e.id
291
+ end
292
+ end
293
+
294
+ end
295
+
296
+ describe "table and column comments" do
297
+
298
+ def create_test_employees_table(table_comment=nil, column_comments={})
299
+ schema_define do
300
+ create_table :test_employees, :comment => table_comment do |t|
301
+ t.string :first_name, :comment => column_comments[:first_name]
302
+ t.string :last_name, :comment => column_comments[:last_name]
303
+ end
304
+ end
305
+ end
306
+
307
+ before(:all) do
308
+ @conn = ActiveRecord::Base.connection
309
+ end
310
+
311
+ after(:each) do
312
+ schema_define do
313
+ drop_table :test_employees
314
+ end
315
+ Object.send(:remove_const, "TestEmployee")
316
+ ActiveRecord::Base.table_name_prefix = nil
317
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
318
+ end
319
+
320
+ it "should create table with table comment" do
321
+ table_comment = "Test Employees"
322
+ create_test_employees_table(table_comment)
323
+ class ::TestEmployee < ActiveRecord::Base; end
324
+
325
+ @conn.table_comment("test_employees").should == table_comment
326
+ TestEmployee.table_comment.should == table_comment
327
+ end
328
+
329
+ it "should create table with columns comment" do
330
+ column_comments = {:first_name => "Given Name", :last_name => "Surname"}
331
+ create_test_employees_table(nil, column_comments)
332
+ class ::TestEmployee < ActiveRecord::Base; end
333
+
334
+ [:first_name, :last_name].each do |attr|
335
+ @conn.column_comment("test_employees", attr.to_s).should == column_comments[attr]
336
+ end
337
+ [:first_name, :last_name].each do |attr|
338
+ TestEmployee.columns_hash[attr.to_s].comment.should == column_comments[attr]
339
+ end
340
+ end
341
+
342
+ it "should create table with table and columns comment and custom table name prefix" do
343
+ ActiveRecord::Base.table_name_prefix = "xxx_"
344
+ table_comment = "Test Employees"
345
+ column_comments = {:first_name => "Given Name", :last_name => "Surname"}
346
+ create_test_employees_table(table_comment, column_comments)
347
+ class ::TestEmployee < ActiveRecord::Base; end
348
+
349
+ @conn.table_comment(TestEmployee.table_name).should == table_comment
350
+ TestEmployee.table_comment.should == table_comment
351
+ [:first_name, :last_name].each do |attr|
352
+ @conn.column_comment(TestEmployee.table_name, attr.to_s).should == column_comments[attr]
353
+ end
354
+ [:first_name, :last_name].each do |attr|
355
+ TestEmployee.columns_hash[attr.to_s].comment.should == column_comments[attr]
356
+ end
357
+ end
358
+
359
+ end
360
+
361
+ describe "rename tables and sequences" do
362
+ before(:each) do
363
+ @conn = ActiveRecord::Base.connection
364
+ schema_define do
365
+ drop_table :test_employees rescue nil
366
+ drop_table :new_test_employees rescue nil
367
+ create_table :test_employees do |t|
368
+ t.string :first_name
369
+ t.string :last_name
370
+ end
371
+ end
372
+ end
373
+
374
+ after(:each) do
375
+ schema_define do
376
+ drop_table :test_employees rescue nil
377
+ drop_table :new_test_employees rescue nil
378
+ end
379
+ end
380
+
381
+ it "should rename table name with new one" do
382
+ lambda do
383
+ @conn.rename_table("test_employees","new_test_employees")
384
+ end.should_not raise_error
385
+ end
386
+
387
+ it "should raise error when new table name length is too long" do
388
+ lambda do
389
+ @conn.rename_table("test_employees","a"*31)
390
+ end.should raise_error
391
+ end
392
+
393
+ it "should raise error when new sequence name length is too long" do
394
+ lambda do
395
+ @conn.rename_table("test_employees","a"*27)
396
+ end.should raise_error
397
+ end
398
+
399
+ end
400
+
401
+ describe "create triggers" do
402
+
403
+ before(:all) do
404
+ @conn = ActiveRecord::Base.connection
405
+ schema_define do
406
+ create_table :test_employees do |t|
407
+ t.string :first_name
408
+ t.string :last_name
409
+ end
410
+ end
411
+ class ::TestEmployee < ActiveRecord::Base; end
412
+ end
413
+
414
+ after(:all) do
415
+ schema_define do
416
+ drop_table :test_employees
417
+ end
418
+ Object.send(:remove_const, "TestEmployee")
419
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
420
+ end
421
+
422
+ it "should create table trigger with :new reference" do
423
+ lambda do
424
+ @conn.execute <<-SQL
425
+ CREATE OR REPLACE TRIGGER test_employees_pkt
426
+ BEFORE INSERT ON test_employees FOR EACH ROW
427
+ BEGIN
428
+ IF inserting THEN
429
+ IF :new.id IS NULL THEN
430
+ SELECT test_employees_seq.NEXTVAL INTO :new.id FROM dual;
431
+ END IF;
432
+ END IF;
433
+ END;
434
+ SQL
435
+ end.should_not raise_error
436
+ end
437
+ end
438
+
439
+ describe "add index" do
440
+ before(:all) do
441
+ @conn = ActiveRecord::Base.connection
442
+ end
443
+
444
+ it "should return default index name if it is not larger than 30 characters" do
445
+ @conn.index_name("employees", :column => "first_name").should == "index_employees_on_first_name"
446
+ end
447
+
448
+ it "should return shortened index name by removing 'index', 'on' and 'and' keywords" do
449
+ @conn.index_name("employees", :column => ["first_name", "email"]).should == "i_employees_first_name_email"
450
+ end
451
+
452
+ it "should return shortened index name by shortening table and column names" do
453
+ @conn.index_name("employees", :column => ["first_name", "last_name"]).should == "i_emp_fir_nam_las_nam"
454
+ end
455
+
456
+ it "should raise error if too large index name cannot be shortened" do
457
+ @conn.index_name("test_employees", :column => ["first_name", "middle_name", "last_name"]).should ==
458
+ 'i'+Digest::SHA1.hexdigest("index_test_employees_on_first_name_and_middle_name_and_last_name")[0,29]
459
+ end
460
+
461
+ end
462
+
463
+ describe "rename index" do
464
+ before(:each) do
465
+ @conn = ActiveRecord::Base.connection
466
+ schema_define do
467
+ create_table :test_employees do |t|
468
+ t.string :first_name
469
+ t.string :last_name
470
+ end
471
+ add_index :test_employees, :first_name
472
+ end
473
+ class ::TestEmployee < ActiveRecord::Base; end
474
+ end
475
+
476
+ after(:each) do
477
+ schema_define do
478
+ drop_table :test_employees
479
+ end
480
+ Object.send(:remove_const, "TestEmployee")
481
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
482
+ end
483
+
484
+ it "should raise error when current index name and new index name are identical" do
485
+ lambda do
486
+ @conn.rename_index("test_employees","i_test_employees_first_name","i_test_employees_first_name")
487
+ end.should raise_error
488
+ end
489
+
490
+ it "should raise error when new index name length is too long" do
491
+ lambda do
492
+ @conn.rename_index("test_employees","i_test_employees_first_name","a"*31)
493
+ end.should raise_error
494
+ end
495
+
496
+ it "should raise error when current index name does not exist" do
497
+ lambda do
498
+ @conn.rename_index("test_employees","nonexist_index_name","new_index_name")
499
+ end.should raise_error
500
+ end
501
+
502
+ it "should rename index name with new one" do
503
+ lambda do
504
+ @conn.rename_index("test_employees","i_test_employees_first_name","new_index_name")
505
+ end.should_not raise_error
506
+ end
507
+ end
508
+
509
+ describe "ignore options for LOB columns" do
510
+ after(:each) do
511
+ schema_define do
512
+ drop_table :test_posts
513
+ end
514
+ end
515
+
516
+ it "should ignore :limit option for :text column" do
517
+ lambda do
518
+ schema_define do
519
+ create_table :test_posts, :force => true do |t|
520
+ t.text :body, :limit => 10000
521
+ end
522
+ end
523
+ end.should_not raise_error
524
+ end
525
+
526
+ it "should ignore :limit option for :binary column" do
527
+ lambda do
528
+ schema_define do
529
+ create_table :test_posts, :force => true do |t|
530
+ t.binary :picture, :limit => 10000
531
+ end
532
+ end
533
+ end.should_not raise_error
534
+ end
535
+
536
+ end
537
+
538
+ describe "foreign key constraints" do
539
+ before(:each) do
540
+ schema_define do
541
+ create_table :test_posts, :force => true do |t|
542
+ t.string :title
543
+ end
544
+ create_table :test_comments, :force => true do |t|
545
+ t.string :body, :limit => 4000
546
+ t.references :test_post
547
+ t.integer :post_id
548
+ end
549
+ end
550
+ class ::TestPost < ActiveRecord::Base
551
+ has_many :test_comments
552
+ end
553
+ class ::TestComment < ActiveRecord::Base
554
+ belongs_to :test_post
555
+ end
556
+ end
557
+
558
+ after(:each) do
559
+ Object.send(:remove_const, "TestPost")
560
+ Object.send(:remove_const, "TestComment")
561
+ schema_define do
562
+ drop_table :test_comments rescue nil
563
+ drop_table :test_posts rescue nil
564
+ end
565
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
566
+ end
567
+
568
+ it "should add foreign key" do
569
+ schema_define do
570
+ add_foreign_key :test_comments, :test_posts
571
+ end
572
+ lambda do
573
+ TestComment.create(:body => "test", :test_post_id => 1)
574
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291.*\.TEST_COMMENTS_TEST_POST_ID_FK/}
575
+ end
576
+
577
+ it "should add foreign key with name" do
578
+ schema_define do
579
+ add_foreign_key :test_comments, :test_posts, :name => "comments_posts_fk"
580
+ end
581
+ lambda do
582
+ TestComment.create(:body => "test", :test_post_id => 1)
583
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291.*\.COMMENTS_POSTS_FK/}
584
+ end
585
+
586
+ it "should add foreign key with long name which is shortened" do
587
+ schema_define do
588
+ add_foreign_key :test_comments, :test_posts, :name => "test_comments_test_post_id_foreign_key"
589
+ end
590
+ lambda do
591
+ TestComment.create(:body => "test", :test_post_id => 1)
592
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291.*\.TES_COM_TES_POS_ID_FOR_KEY/}
593
+ end
594
+
595
+ it "should add foreign key with very long name which is shortened" do
596
+ schema_define do
597
+ add_foreign_key :test_comments, :test_posts, :name => "long_prefix_test_comments_test_post_id_foreign_key"
598
+ end
599
+ lambda do
600
+ TestComment.create(:body => "test", :test_post_id => 1)
601
+ end.should raise_error() {|e| e.message.should =~
602
+ /ORA-02291.*\.C#{Digest::SHA1.hexdigest("long_prefix_test_comments_test_post_id_foreign_key")[0,29].upcase}/}
603
+ end
604
+
605
+ it "should add foreign key with column" do
606
+ schema_define do
607
+ add_foreign_key :test_comments, :test_posts, :column => "post_id"
608
+ end
609
+ lambda do
610
+ TestComment.create(:body => "test", :post_id => 1)
611
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291.*\.TEST_COMMENTS_POST_ID_FK/}
612
+ end
613
+
614
+ it "should add foreign key with delete dependency" do
615
+ schema_define do
616
+ add_foreign_key :test_comments, :test_posts, :dependent => :delete
617
+ end
618
+ p = TestPost.create(:title => "test")
619
+ c = TestComment.create(:body => "test", :test_post => p)
620
+ TestPost.delete(p.id)
621
+ TestComment.find_by_id(c.id).should be_nil
622
+ end
623
+
624
+ it "should add foreign key with nullify dependency" do
625
+ schema_define do
626
+ add_foreign_key :test_comments, :test_posts, :dependent => :nullify
627
+ end
628
+ p = TestPost.create(:title => "test")
629
+ c = TestComment.create(:body => "test", :test_post => p)
630
+ TestPost.delete(p.id)
631
+ TestComment.find_by_id(c.id).test_post_id.should be_nil
632
+ end
633
+
634
+ it "should add a composite foreign key" do
635
+ schema_define do
636
+ add_column :test_posts, :baz_id, :integer
637
+ add_column :test_posts, :fooz_id, :integer
638
+
639
+ execute <<-SQL
640
+ ALTER TABLE TEST_POSTS
641
+ ADD CONSTRAINT UK_FOOZ_BAZ UNIQUE (BAZ_ID,FOOZ_ID)
642
+ SQL
643
+
644
+ add_column :test_comments, :baz_id, :integer
645
+ add_column :test_comments, :fooz_id, :integer
646
+
647
+ add_foreign_key :test_comments, :test_posts, :columns => ["baz_id", "fooz_id"]
648
+ end
649
+
650
+ lambda do
651
+ TestComment.create(:body => "test", :fooz_id => 1, :baz_id => 1)
652
+ end.should raise_error() {|e| e.message.should =~
653
+ /ORA-02291.*\.TES_COM_BAZ_ID_FOO_ID_FK/}
654
+ end
655
+
656
+ it "should add a composite foreign key with name" do
657
+ schema_define do
658
+ add_column :test_posts, :baz_id, :integer
659
+ add_column :test_posts, :fooz_id, :integer
660
+
661
+ execute <<-SQL
662
+ ALTER TABLE TEST_POSTS
663
+ ADD CONSTRAINT UK_FOOZ_BAZ UNIQUE (BAZ_ID,FOOZ_ID)
664
+ SQL
665
+
666
+ add_column :test_comments, :baz_id, :integer
667
+ add_column :test_comments, :fooz_id, :integer
668
+
669
+ add_foreign_key :test_comments, :test_posts, :columns => ["baz_id", "fooz_id"], :name => 'comments_posts_baz_fooz_fk'
670
+ end
671
+
672
+ lambda do
673
+ TestComment.create(:body => "test", :baz_id => 1, :fooz_id => 1)
674
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291.*\.COMMENTS_POSTS_BAZ_FOOZ_FK/}
675
+ end
676
+
677
+ it "should remove foreign key by table name" do
678
+ schema_define do
679
+ add_foreign_key :test_comments, :test_posts
680
+ remove_foreign_key :test_comments, :test_posts
681
+ end
682
+ lambda do
683
+ TestComment.create(:body => "test", :test_post_id => 1)
684
+ end.should_not raise_error
685
+ end
686
+
687
+ it "should remove foreign key by constraint name" do
688
+ schema_define do
689
+ add_foreign_key :test_comments, :test_posts, :name => "comments_posts_fk"
690
+ remove_foreign_key :test_comments, :name => "comments_posts_fk"
691
+ end
692
+ lambda do
693
+ TestComment.create(:body => "test", :test_post_id => 1)
694
+ end.should_not raise_error
695
+ end
696
+
697
+ it "should remove foreign key by column name" do
698
+ schema_define do
699
+ add_foreign_key :test_comments, :test_posts
700
+ remove_foreign_key :test_comments, :column => "test_post_id"
701
+ end
702
+ lambda do
703
+ TestComment.create(:body => "test", :test_post_id => 1)
704
+ end.should_not raise_error
705
+ end
706
+
707
+ end
708
+
709
+ describe "foreign key in table definition" do
710
+ before(:each) do
711
+ schema_define do
712
+ create_table :test_posts, :force => true do |t|
713
+ t.string :title
714
+ end
715
+ end
716
+ class ::TestPost < ActiveRecord::Base
717
+ has_many :test_comments
718
+ end
719
+ class ::TestComment < ActiveRecord::Base
720
+ belongs_to :test_post
721
+ end
722
+ end
723
+
724
+ after(:each) do
725
+ Object.send(:remove_const, "TestPost")
726
+ Object.send(:remove_const, "TestComment")
727
+ schema_define do
728
+ drop_table :test_comments rescue nil
729
+ drop_table :test_posts rescue nil
730
+ end
731
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
732
+ end
733
+
734
+ it "should add foreign key in create_table" do
735
+ schema_define do
736
+ create_table :test_comments, :force => true do |t|
737
+ t.string :body, :limit => 4000
738
+ t.references :test_post
739
+ t.foreign_key :test_posts
740
+ end
741
+ end
742
+ lambda do
743
+ TestComment.create(:body => "test", :test_post_id => 1)
744
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291/}
745
+ end
746
+
747
+ it "should add foreign key in create_table references" do
748
+ schema_define do
749
+ create_table :test_comments, :force => true do |t|
750
+ t.string :body, :limit => 4000
751
+ t.references :test_post, :foreign_key => true
752
+ end
753
+ end
754
+ lambda do
755
+ TestComment.create(:body => "test", :test_post_id => 1)
756
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291/}
757
+ end
758
+
759
+ it "should add foreign key in change_table" do
760
+ return pending("Not in this ActiveRecord version") unless ENV['RAILS_GEM_VERSION'] >= '2.1'
761
+ schema_define do
762
+ create_table :test_comments, :force => true do |t|
763
+ t.string :body, :limit => 4000
764
+ t.references :test_post
765
+ end
766
+ change_table :test_comments do |t|
767
+ t.foreign_key :test_posts
768
+ end
769
+ end
770
+ lambda do
771
+ TestComment.create(:body => "test", :test_post_id => 1)
772
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291/}
773
+ end
774
+
775
+ it "should add foreign key in change_table references" do
776
+ return pending("Not in this ActiveRecord version") unless ENV['RAILS_GEM_VERSION'] >= '2.1'
777
+ schema_define do
778
+ create_table :test_comments, :force => true do |t|
779
+ t.string :body, :limit => 4000
780
+ end
781
+ change_table :test_comments do |t|
782
+ t.references :test_post, :foreign_key => true
783
+ end
784
+ end
785
+ lambda do
786
+ TestComment.create(:body => "test", :test_post_id => 1)
787
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291/}
788
+ end
789
+
790
+ it "should remove foreign key by table name" do
791
+ return pending("Not in this ActiveRecord version") unless ENV['RAILS_GEM_VERSION'] >= '2.1'
792
+ schema_define do
793
+ create_table :test_comments, :force => true do |t|
794
+ t.string :body, :limit => 4000
795
+ t.references :test_post
796
+ end
797
+ change_table :test_comments do |t|
798
+ t.foreign_key :test_posts
799
+ end
800
+ change_table :test_comments do |t|
801
+ t.remove_foreign_key :test_posts
802
+ end
803
+ end
804
+ lambda do
805
+ TestComment.create(:body => "test", :test_post_id => 1)
806
+ end.should_not raise_error
807
+ end
808
+
809
+ end
810
+
811
+ describe "disable referential integrity" do
812
+ before(:all) do
813
+ @conn = ActiveRecord::Base.connection
814
+ end
815
+
816
+ before(:each) do
817
+ schema_define do
818
+ create_table :test_posts, :force => true do |t|
819
+ t.string :title
820
+ end
821
+ create_table :test_comments, :force => true do |t|
822
+ t.string :body, :limit => 4000
823
+ t.references :test_post, :foreign_key => true
824
+ end
825
+ end
826
+ end
827
+
828
+ after(:each) do
829
+ schema_define do
830
+ drop_table :test_comments rescue nil
831
+ drop_table :test_posts rescue nil
832
+ end
833
+ end
834
+
835
+ it "should disable all foreign keys" do
836
+ lambda do
837
+ @conn.execute "INSERT INTO test_comments (id, body, test_post_id) VALUES (1, 'test', 1)"
838
+ end.should raise_error
839
+ @conn.disable_referential_integrity do
840
+ lambda do
841
+ @conn.execute "INSERT INTO test_comments (id, body, test_post_id) VALUES (2, 'test', 2)"
842
+ @conn.execute "INSERT INTO test_posts (id, title) VALUES (2, 'test')"
843
+ end.should_not raise_error
844
+ end
845
+ lambda do
846
+ @conn.execute "INSERT INTO test_comments (id, body, test_post_id) VALUES (3, 'test', 3)"
847
+ end.should raise_error
848
+ end
849
+
850
+ end
851
+
852
+ describe "synonyms" do
853
+ before(:all) do
854
+ @conn = ActiveRecord::Base.connection
855
+ @db_link = "db_link"
856
+ @username = @db_link_username = CONNECTION_PARAMS[:username]
857
+ @db_link_password = CONNECTION_PARAMS[:password]
858
+ @db_link_database = CONNECTION_PARAMS[:database]
859
+ @conn.execute "DROP DATABASE LINK #{@db_link}" rescue nil
860
+ @conn.execute "CREATE DATABASE LINK #{@db_link} CONNECT TO #{@db_link_username} IDENTIFIED BY \"#{@db_link_password}\" USING '#{@db_link_database}'"
861
+ schema_define do
862
+ create_table :test_posts, :force => true do |t|
863
+ t.string :title
864
+ end
865
+ end
866
+ end
867
+
868
+ after(:all) do
869
+ schema_define do
870
+ drop_table :test_posts
871
+ end
872
+ @conn.execute "DROP DATABASE LINK #{@db_link}" rescue nil
873
+ end
874
+
875
+ before(:each) do
876
+ class ::TestPost < ActiveRecord::Base
877
+ if self.respond_to?(:table_name=)
878
+ self.table_name = "synonym_to_posts"
879
+ else
880
+ set_table_name "synonym_to_posts"
881
+ end
882
+ end
883
+ end
884
+
885
+ after(:each) do
886
+ Object.send(:remove_const, "TestPost")
887
+ schema_define do
888
+ remove_synonym :synonym_to_posts
889
+ remove_synonym :synonym_to_posts_seq
890
+ end
891
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
892
+ end
893
+
894
+ it "should create synonym to table and sequence" do
895
+ schema_name = @username
896
+ schema_define do
897
+ add_synonym :synonym_to_posts, "#{schema_name}.test_posts", :force => true
898
+ add_synonym :synonym_to_posts_seq, "#{schema_name}.test_posts_seq", :force => true
899
+ end
900
+ lambda do
901
+ TestPost.create(:title => "test")
902
+ end.should_not raise_error
903
+ end
904
+
905
+ it "should create synonym to table over database link" do
906
+ db_link = @db_link
907
+ schema_define do
908
+ add_synonym :synonym_to_posts, "test_posts@#{db_link}", :force => true
909
+ add_synonym :synonym_to_posts_seq, "test_posts_seq@#{db_link}", :force => true
910
+ end
911
+ lambda do
912
+ TestPost.create(:title => "test")
913
+ end.should_not raise_error
914
+ end
915
+
916
+ end
917
+
918
+ describe "alter columns with column cache" do
919
+ include LoggerSpecHelper
920
+
921
+ before(:all) do
922
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = true
923
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces.delete(:clob)
924
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces.delete(:blob)
925
+ end
926
+
927
+ after(:all) do
928
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.cache_columns = nil
929
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces.delete(:clob)
930
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces.delete(:blob)
931
+ end
932
+
933
+ before(:each) do
934
+ schema_define do
935
+ create_table :test_posts, :force => true do |t|
936
+ t.string :title, :null => false
937
+ t.string :content
938
+ end
939
+ end
940
+ class ::TestPost < ActiveRecord::Base; end
941
+ TestPost.columns_hash['title'].null.should be_false
942
+ end
943
+
944
+ after(:each) do
945
+ Object.send(:remove_const, "TestPost")
946
+ schema_define { drop_table :test_posts }
947
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
948
+ end
949
+
950
+ it "should change column to nullable" do
951
+ schema_define do
952
+ change_column :test_posts, :title, :string, :null => true
953
+ end
954
+ TestPost.reset_column_information
955
+ TestPost.columns_hash['title'].null.should be_true
956
+ end
957
+
958
+ it "should add column" do
959
+ schema_define do
960
+ add_column :test_posts, :body, :string
961
+ end
962
+ TestPost.reset_column_information
963
+ TestPost.columns_hash['body'].should_not be_nil
964
+ end
965
+
966
+ it "should add lob column with non_default tablespace" do
967
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[:clob] = DATABASE_NON_DEFAULT_TABLESPACE
968
+ schema_define do
969
+ add_column :test_posts, :body, :text
970
+ end
971
+ TestPost.connection.select_value("SELECT tablespace_name FROM user_lobs WHERE table_name='TEST_POSTS' and column_name = 'BODY'").should == DATABASE_NON_DEFAULT_TABLESPACE
972
+ end
973
+
974
+ it "should add blob column with non_default tablespace" do
975
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[:blob] = DATABASE_NON_DEFAULT_TABLESPACE
976
+ schema_define do
977
+ add_column :test_posts, :attachment, :binary
978
+ end
979
+ TestPost.connection.select_value("SELECT tablespace_name FROM user_lobs WHERE table_name='TEST_POSTS' and column_name = 'ATTACHMENT'").should == DATABASE_NON_DEFAULT_TABLESPACE
980
+ end
981
+
982
+ it "should rename column" do
983
+ schema_define do
984
+ rename_column :test_posts, :title, :subject
985
+ end
986
+ TestPost.reset_column_information
987
+ TestPost.columns_hash['subject'].should_not be_nil
988
+ TestPost.columns_hash['title'].should be_nil
989
+ end
990
+
991
+ it "should remove column" do
992
+ schema_define do
993
+ remove_column :test_posts, :title
994
+ end
995
+ TestPost.reset_column_information
996
+ TestPost.columns_hash['title'].should be_nil
997
+ end
998
+
999
+ it "should remove column when using change_table" do
1000
+ schema_define do
1001
+ change_table :test_posts do |t|
1002
+ t.remove :title
1003
+ end
1004
+ end
1005
+ TestPost.reset_column_information
1006
+ TestPost.columns_hash['title'].should be_nil
1007
+ end
1008
+
1009
+ it "should remove multiple columns when using change_table" do
1010
+ schema_define do
1011
+ change_table :test_posts do |t|
1012
+ t.remove :title, :content
1013
+ end
1014
+ end
1015
+ TestPost.reset_column_information
1016
+ TestPost.columns_hash['title'].should be_nil
1017
+ TestPost.columns_hash['content'].should be_nil
1018
+ end
1019
+ end
1020
+
1021
+ describe 'virtual columns in create_table' do
1022
+ before(:each) do
1023
+ pending "Not supported in this database version" unless @oracle11g
1024
+ end
1025
+
1026
+ it 'should create virtual column with old syntax' do
1027
+ schema_define do
1028
+ create_table :test_fractions, :force => true do |t|
1029
+ t.integer :field1
1030
+ t.virtual :field2, :default => 'field1 + 1'
1031
+ end
1032
+ end
1033
+ class ::TestFraction < ActiveRecord::Base
1034
+ if self.respond_to?(:table_name=)
1035
+ self.table_name = "test_fractions"
1036
+ else
1037
+ set_table_name "test_fractions"
1038
+ end
1039
+ end
1040
+
1041
+ TestFraction.reset_column_information
1042
+ tf = TestFraction.columns.detect { |c| c.virtual? }
1043
+ tf.should_not be nil
1044
+ tf.name.should == "field2"
1045
+ tf.virtual?.should be true
1046
+ lambda do
1047
+ tf = TestFraction.new(:field1=>10)
1048
+ tf.field2.should be nil # not whatever is in DATA_DEFAULT column
1049
+ tf.save!
1050
+ tf.reload
1051
+ end.should_not raise_error
1052
+ tf.field2.to_i.should == 11
1053
+
1054
+ schema_define do
1055
+ drop_table :test_fractions
1056
+ end
1057
+ end
1058
+
1059
+ it 'should raise error if column expression is not provided' do
1060
+ lambda {
1061
+ schema_define do
1062
+ create_table :test_fractions do |t|
1063
+ t.integer :field1
1064
+ t.virtual :field2
1065
+ end
1066
+ end
1067
+ }.should raise_error
1068
+ end
1069
+ end
1070
+
1071
+ describe 'virtual columns' do
1072
+ before(:each) do
1073
+ pending "Not supported in this database version" unless @oracle11g
1074
+ expr = "( numerator/NULLIF(denominator,0) )*100"
1075
+ schema_define do
1076
+ create_table :test_fractions, :force => true do |t|
1077
+ t.integer :numerator, :default=>0
1078
+ t.integer :denominator, :default=>0
1079
+ t.virtual :percent, :as => expr
1080
+ end
1081
+ end
1082
+ class ::TestFraction < ActiveRecord::Base
1083
+ if self.respond_to?(:table_name=)
1084
+ self.table_name = "test_fractions"
1085
+ else
1086
+ set_table_name "test_fractions"
1087
+ end
1088
+ end
1089
+ TestFraction.reset_column_information
1090
+ end
1091
+
1092
+ after(:each) do
1093
+ if @oracle11g
1094
+ schema_define do
1095
+ drop_table :test_fractions
1096
+ end
1097
+ end
1098
+ end
1099
+
1100
+ it 'should include virtual columns and not try to update them' do
1101
+ tf = TestFraction.columns.detect { |c| c.virtual? }
1102
+ tf.should_not be nil
1103
+ tf.name.should == "percent"
1104
+ tf.virtual?.should be true
1105
+ lambda do
1106
+ tf = TestFraction.new(:numerator=>20, :denominator=>100)
1107
+ tf.percent.should be nil # not whatever is in DATA_DEFAULT column
1108
+ tf.save!
1109
+ tf.reload
1110
+ end.should_not raise_error
1111
+ tf.percent.to_i.should == 20
1112
+ end
1113
+
1114
+ it 'should add virtual column' do
1115
+ schema_define do
1116
+ add_column :test_fractions, :rem, :virtual, :as => 'remainder(numerator, NULLIF(denominator,0))'
1117
+ end
1118
+ TestFraction.reset_column_information
1119
+ tf = TestFraction.columns.detect { |c| c.name == 'rem' }
1120
+ tf.should_not be nil
1121
+ tf.virtual?.should be true
1122
+ lambda do
1123
+ tf = TestFraction.new(:numerator=>7, :denominator=>5)
1124
+ tf.rem.should be nil
1125
+ tf.save!
1126
+ tf.reload
1127
+ end.should_not raise_error
1128
+ tf.rem.to_i.should == 2
1129
+ end
1130
+
1131
+ it 'should add virtual column with explicit type' do
1132
+ schema_define do
1133
+ add_column :test_fractions, :expression, :virtual, :as => "TO_CHAR(numerator) || '/' || TO_CHAR(denominator)", :type => :string, :limit => 100
1134
+ end
1135
+ TestFraction.reset_column_information
1136
+ tf = TestFraction.columns.detect { |c| c.name == 'expression' }
1137
+ tf.should_not be nil
1138
+ tf.virtual?.should be true
1139
+ tf.type.should be :string
1140
+ tf.limit.should be 100
1141
+ lambda do
1142
+ tf = TestFraction.new(:numerator=>7, :denominator=>5)
1143
+ tf.expression.should be nil
1144
+ tf.save!
1145
+ tf.reload
1146
+ end.should_not raise_error
1147
+ tf.expression.should == '7/5'
1148
+ end
1149
+
1150
+ it 'should change virtual column definition' do
1151
+ schema_define do
1152
+ change_column :test_fractions, :percent, :virtual,
1153
+ :as => "ROUND((numerator/NULLIF(denominator,0))*100, 2)", :type => :decimal, :precision => 15, :scale => 2
1154
+ end
1155
+ TestFraction.reset_column_information
1156
+ tf = TestFraction.columns.detect { |c| c.name == 'percent' }
1157
+ tf.should_not be nil
1158
+ tf.virtual?.should be true
1159
+ tf.type.should be :decimal
1160
+ tf.precision.should be 15
1161
+ tf.scale.should be 2
1162
+ lambda do
1163
+ tf = TestFraction.new(:numerator=>11, :denominator=>17)
1164
+ tf.percent.should be nil
1165
+ tf.save!
1166
+ tf.reload
1167
+ end.should_not raise_error
1168
+ tf.percent.should == '64.71'.to_d
1169
+ end
1170
+
1171
+ it 'should change virtual column type' do
1172
+ schema_define do
1173
+ change_column :test_fractions, :percent, :virtual, :type => :decimal, :precision => 12, :scale => 5
1174
+ end
1175
+ TestFraction.reset_column_information
1176
+ tf = TestFraction.columns.detect { |c| c.name == 'percent' }
1177
+ tf.should_not be nil
1178
+ tf.virtual?.should be true
1179
+ tf.type.should be :decimal
1180
+ tf.precision.should be 12
1181
+ tf.scale.should be 5
1182
+ lambda do
1183
+ tf = TestFraction.new(:numerator=>11, :denominator=>17)
1184
+ tf.percent.should be nil
1185
+ tf.save!
1186
+ tf.reload
1187
+ end.should_not raise_error
1188
+ tf.percent.should == '64.70588'.to_d
1189
+ end
1190
+ end
1191
+
1192
+ describe "miscellaneous options" do
1193
+ before(:all) do
1194
+ @conn = ActiveRecord::Base.connection
1195
+ end
1196
+
1197
+ before(:each) do
1198
+ @conn.instance_variable_set :@would_execute_sql, @would_execute_sql=''
1199
+ class <<@conn
1200
+ def execute(sql,name=nil); @would_execute_sql << sql << ";\n"; end
1201
+ def index_name_exists?(table_name, index_name, default); default; end
1202
+ end
1203
+ end
1204
+
1205
+ after(:each) do
1206
+ class <<@conn
1207
+ remove_method :execute
1208
+ end
1209
+ @conn.instance_eval{ remove_instance_variable :@would_execute_sql }
1210
+ end
1211
+
1212
+ it "should support the :options option to create_table" do
1213
+ schema_define do
1214
+ create_table :test_posts, :options=>'NOLOGGING', :force => true do |t|
1215
+ t.string :title, :null => false
1216
+ end
1217
+ end
1218
+ @would_execute_sql.should =~ /CREATE +TABLE .* \(.*\) NOLOGGING/
1219
+ end
1220
+
1221
+ it "should support the :tablespace option to create_table" do
1222
+ schema_define do
1223
+ create_table :test_posts, :tablespace=>'bogus', :force => true do |t|
1224
+ t.string :title, :null => false
1225
+ end
1226
+ end
1227
+ @would_execute_sql.should =~ /CREATE +TABLE .* \(.*\) TABLESPACE bogus/
1228
+ end
1229
+
1230
+ describe "creating a table with a tablespace defaults set" do
1231
+ after(:each) do
1232
+ @conn.drop_table :tablespace_tests rescue nil
1233
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces.delete(:table)
1234
+ end
1235
+ it "should use correct tablespace" do
1236
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[:table] = DATABASE_NON_DEFAULT_TABLESPACE
1237
+ @conn.create_table :tablespace_tests do |t|
1238
+ t.integer :id
1239
+ end
1240
+ @would_execute_sql.should =~ /CREATE +TABLE .* \(.*\) TABLESPACE #{DATABASE_NON_DEFAULT_TABLESPACE}/
1241
+ end
1242
+ end
1243
+
1244
+ describe "creating an index-organized table" do
1245
+ after(:each) do
1246
+ @conn.drop_table :tablespace_tests rescue nil
1247
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces.delete(:table)
1248
+ end
1249
+ it "should use correct tablespace" do
1250
+ @conn.create_table :tablespace_tests, :id=>false, :organization=>'INDEX INITRANS 4 COMPRESS 1', :tablespace=>'bogus' do |t|
1251
+ t.integer :id
1252
+ end
1253
+ @would_execute_sql.should =~ /CREATE +TABLE .*\(.*\)\s+ORGANIZATION INDEX INITRANS 4 COMPRESS 1 TABLESPACE bogus/
1254
+ end
1255
+ end
1256
+
1257
+ it "should support the :options option to add_index" do
1258
+ schema_define do
1259
+ add_index :keyboards, :name, :options=>'NOLOGGING'
1260
+ end
1261
+ @would_execute_sql.should =~ /CREATE +INDEX .* ON .* \(.*\) NOLOGGING/
1262
+ end
1263
+
1264
+ it "should support the :tablespace option to add_index" do
1265
+ schema_define do
1266
+ add_index :keyboards, :name, :tablespace=>'bogus'
1267
+ end
1268
+ @would_execute_sql.should =~ /CREATE +INDEX .* ON .* \(.*\) TABLESPACE bogus/
1269
+ end
1270
+
1271
+ it "should use default_tablespaces in add_index" do
1272
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[:index] = DATABASE_NON_DEFAULT_TABLESPACE
1273
+ schema_define do
1274
+ add_index :keyboards, :name
1275
+ end
1276
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces.delete(:index)
1277
+ @would_execute_sql.should =~ /CREATE +INDEX .* ON .* \(.*\) TABLESPACE #{DATABASE_NON_DEFAULT_TABLESPACE}/
1278
+ end
1279
+ end
1280
+ end