pmacs-activerecord-oracle_enhanced-adapter 1.4.2.rc1

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