ctreatma-activerecord-oracle_enhanced-adapter 1.4.1.1

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