activerecord-oracle_enhanced-adapter-with-schema 0.0.1

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