unclebilly-activerecord-oracle_enhanced-adapter 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/History.txt +165 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +32 -0
  4. data/README.rdoc +75 -0
  5. data/Rakefile +49 -0
  6. data/VERSION +1 -0
  7. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
  8. data/lib/active_record/connection_adapters/oracle_enhanced.rake +51 -0
  9. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1723 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +121 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +64 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +39 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +369 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +396 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +164 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced_reserved_words.rb +126 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +177 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +214 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +224 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +11 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced_virtual_column.rb +35 -0
  24. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +610 -0
  25. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb +266 -0
  26. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +206 -0
  27. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +40 -0
  28. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +107 -0
  29. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +984 -0
  30. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +67 -0
  31. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +93 -0
  32. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +25 -0
  33. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +370 -0
  34. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +268 -0
  35. data/spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb +761 -0
  36. data/spec/spec.opts +6 -0
  37. data/spec/spec_helper.rb +130 -0
  38. metadata +149 -0
@@ -0,0 +1,268 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe "OracleEnhancedAdapter original schema dump" do
4
+
5
+ before(:all) do
6
+ if !defined?(RUBY_ENGINE)
7
+ if ActiveRecord::Base.respond_to?(:oracle_connection)
8
+ @old_conn = ActiveRecord::Base.oracle_connection(CONNECTION_PARAMS)
9
+ @old_conn.class.should == ActiveRecord::ConnectionAdapters::OracleAdapter
10
+ end
11
+ elsif RUBY_ENGINE == 'jruby'
12
+ @old_conn = ActiveRecord::Base.jdbc_connection(JDBC_CONNECTION_PARAMS)
13
+ @old_conn.class.should == ActiveRecord::ConnectionAdapters::JdbcAdapter
14
+ end
15
+
16
+ @new_conn = ActiveRecord::Base.oracle_enhanced_connection(CONNECTION_PARAMS)
17
+ @new_conn.class.should == ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter
18
+ end
19
+
20
+ after(:all) do
21
+ # Workaround for undefining callback that was defined by JDBC adapter
22
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
23
+ ActiveRecord::Base.class_eval do
24
+ def after_save_with_oracle_lob
25
+ nil
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ if !defined?(RUBY_ENGINE) && ActiveRecord::Base.respond_to?(:oracle_connection) || defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
32
+ it "should return the same tables list as original oracle adapter" do
33
+ @new_conn.tables.sort.should == @old_conn.tables.sort
34
+ end
35
+
36
+ it "should return the same index list as original oracle adapter" do
37
+ @new_conn.indexes('employees').sort_by(&:name).should == @old_conn.indexes('employees').sort_by(&:name)
38
+ end
39
+
40
+ it "should return the same pk_and_sequence_for as original oracle adapter" do
41
+ if @old_conn.respond_to?(:pk_and_sequence_for)
42
+ @new_conn.tables.each do |t|
43
+ @new_conn.pk_and_sequence_for(t).should == @old_conn.pk_and_sequence_for(t)
44
+ end
45
+ end
46
+ end
47
+
48
+ it "should return the same structure dump as original oracle adapter" do
49
+ @new_conn.structure_dump.split(";\n\n").sort.should == @old_conn.structure_dump.split(";\n\n").sort
50
+ end
51
+
52
+ it "should return the same structure drop as original oracle adapter" do
53
+ @new_conn.structure_drop.split(";\n\n").sort.should == @old_conn.structure_drop.split(";\n\n").sort
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ describe "OracleEnhancedAdapter schema dump" do
60
+ include SchemaSpecHelper
61
+
62
+ before(:all) do
63
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
64
+ @conn = ActiveRecord::Base.connection
65
+ end
66
+
67
+ def standard_dump
68
+ stream = StringIO.new
69
+ ActiveRecord::SchemaDumper.ignore_tables = []
70
+ ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
71
+ stream.string
72
+ end
73
+
74
+ def create_test_posts_table(options = {})
75
+ options.merge! :force => true
76
+ schema_define do
77
+ create_table :test_posts, options do |t|
78
+ t.string :title
79
+ end
80
+ add_index :test_posts, :title
81
+ end
82
+ end
83
+
84
+ def drop_test_posts_table
85
+ schema_define do
86
+ drop_table :test_posts
87
+ end
88
+ rescue
89
+ nil
90
+ end
91
+
92
+ describe "table prefixes and suffixes" do
93
+ after(:each) do
94
+ drop_test_posts_table
95
+ @conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if @conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
96
+ ActiveRecord::Base.table_name_prefix = ''
97
+ ActiveRecord::Base.table_name_suffix = ''
98
+ end
99
+
100
+ it "should remove table prefix in schema dump" do
101
+ ActiveRecord::Base.table_name_prefix = 'xxx_'
102
+ create_test_posts_table
103
+ standard_dump.should =~ /create_table "test_posts".*add_index "test_posts"/m
104
+ end
105
+
106
+ it "should remove table suffix in schema dump" do
107
+ ActiveRecord::Base.table_name_suffix = '_xxx'
108
+ create_test_posts_table
109
+ standard_dump.should =~ /create_table "test_posts".*add_index "test_posts"/m
110
+ end
111
+
112
+ it "should not include schema_migrations table with prefix in schema dump" do
113
+ ActiveRecord::Base.table_name_prefix = 'xxx_'
114
+ @conn.initialize_schema_migrations_table
115
+ standard_dump.should_not =~ /schema_migrations/
116
+ end
117
+
118
+ it "should not include schema_migrations table with suffix in schema dump" do
119
+ ActiveRecord::Base.table_name_suffix = '_xxx'
120
+ @conn.initialize_schema_migrations_table
121
+ standard_dump.should_not =~ /schema_migrations/
122
+ end
123
+
124
+ end
125
+
126
+ describe "table with non-default primary key" do
127
+ after(:each) do
128
+ drop_test_posts_table
129
+ end
130
+
131
+ it "should include non-default primary key in schema dump" do
132
+ create_test_posts_table(:primary_key => 'post_id')
133
+ standard_dump.should =~ /create_table "test_posts", :primary_key => "post_id"/
134
+ end
135
+
136
+ end
137
+
138
+ describe "table with primary key trigger" do
139
+
140
+ after(:each) do
141
+ drop_test_posts_table
142
+ @conn.clear_prefetch_primary_key
143
+ end
144
+
145
+ it "should include primary key trigger in schema dump" do
146
+ create_test_posts_table(:primary_key_trigger => true)
147
+ standard_dump.should =~ /create_table "test_posts".*add_primary_key_trigger "test_posts"/m
148
+ end
149
+
150
+ it "should include primary key trigger with non-default primary key in schema dump" do
151
+ create_test_posts_table(:primary_key_trigger => true, :primary_key => 'post_id')
152
+ standard_dump.should =~ /create_table "test_posts", :primary_key => "post_id".*add_primary_key_trigger "test_posts", :primary_key => "post_id"/m
153
+ end
154
+
155
+ end
156
+
157
+ describe "foreign key constraints" do
158
+ before(:all) do
159
+ schema_define do
160
+ create_table :test_posts, :force => true do |t|
161
+ t.string :title
162
+ end
163
+ create_table :test_comments, :force => true do |t|
164
+ t.string :body, :limit => 4000
165
+ t.references :test_post
166
+ end
167
+ end
168
+ end
169
+
170
+ after(:each) do
171
+ schema_define do
172
+ remove_foreign_key :test_comments, :test_posts
173
+ end
174
+ end
175
+ after(:all) do
176
+ schema_define do
177
+ drop_table :test_comments rescue nil
178
+ drop_table :test_posts rescue nil
179
+ end
180
+ end
181
+
182
+ it "should include foreign key in schema dump" do
183
+ schema_define do
184
+ add_foreign_key :test_comments, :test_posts
185
+ end
186
+ standard_dump.should =~ /add_foreign_key "test_comments", "test_posts", :name => "test_comments_test_post_id_fk"/
187
+ end
188
+
189
+ it "should include foreign key with delete dependency in schema dump" do
190
+ schema_define do
191
+ add_foreign_key :test_comments, :test_posts, :dependent => :delete
192
+ end
193
+ standard_dump.should =~ /add_foreign_key "test_comments", "test_posts", :name => "test_comments_test_post_id_fk", :dependent => :delete/
194
+ end
195
+
196
+ it "should include foreign key with nullify dependency in schema dump" do
197
+ schema_define do
198
+ add_foreign_key :test_comments, :test_posts, :dependent => :nullify
199
+ end
200
+ standard_dump.should =~ /add_foreign_key "test_comments", "test_posts", :name => "test_comments_test_post_id_fk", :dependent => :nullify/
201
+ end
202
+
203
+ end
204
+
205
+ describe 'virtual columns' do
206
+ before(:all) do
207
+ schema_define do
208
+ create_table :test_names, :force => true do |t|
209
+ t.string :first_name
210
+ t.string :last_name
211
+ t.virtual :full_name, :default=>"first_name || ', ' || last_name"
212
+ end
213
+ end
214
+ end
215
+ before(:each) do
216
+ class ::TestName < ActiveRecord::Base
217
+ set_table_name "test_names"
218
+
219
+ end
220
+ end
221
+
222
+ after(:all) do
223
+ schema_define do
224
+ drop_table :test_names
225
+ end
226
+ end
227
+
228
+ it 'should dump correctly' do
229
+ standard_dump.should =~ /t.virtual "full_name",(\s*):limit => 512,(\s*):default => "/
230
+ end
231
+
232
+ end
233
+
234
+ describe "synonyms" do
235
+ after(:each) do
236
+ schema_define do
237
+ remove_synonym :test_synonym
238
+ end
239
+ end
240
+
241
+ it "should include synonym to other schema table in schema dump" do
242
+ schema_define do
243
+ add_synonym :test_synonym, "schema_name.table_name", :force => true
244
+ end
245
+ standard_dump.should =~ /add_synonym "test_synonym", "schema_name.table_name", :force => true/
246
+ end
247
+
248
+ it "should include synonym to other database table in schema dump" do
249
+ schema_define do
250
+ add_synonym :test_synonym, "table_name@link_name", :force => true
251
+ end
252
+ standard_dump.should =~ /add_synonym "test_synonym", "table_name@link_name(.+)", :force => true/
253
+ end
254
+
255
+ end
256
+
257
+ describe "temporary tables" do
258
+ after(:each) do
259
+ drop_test_posts_table
260
+ end
261
+
262
+ it "should include temporary options" do
263
+ create_test_posts_table(:temporary => true)
264
+ standard_dump.should =~ /create_table "test_posts", :temporary => true/
265
+ end
266
+ end
267
+ end
268
+
@@ -0,0 +1,761 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../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
+ @conn = ActiveRecord::Base.connection
9
+ end
10
+
11
+ describe "table and sequence creation with non-default primary key" do
12
+
13
+ before(:all) do
14
+ schema_define do
15
+ create_table :keyboards, :force => true, :id => false do |t|
16
+ t.primary_key :key_number
17
+ t.string :name
18
+ end
19
+ create_table :id_keyboards, :force => true do |t|
20
+ t.string :name
21
+ end
22
+ end
23
+ class ::Keyboard < ActiveRecord::Base
24
+ set_primary_key :key_number
25
+ end
26
+ class ::IdKeyboard < ActiveRecord::Base
27
+ end
28
+ end
29
+
30
+ after(:all) do
31
+ schema_define do
32
+ drop_table :keyboards
33
+ drop_table :id_keyboards
34
+ end
35
+ Object.send(:remove_const, "Keyboard")
36
+ Object.send(:remove_const, "IdKeyboard")
37
+ end
38
+
39
+ it "should create sequence for non-default primary key" do
40
+ ActiveRecord::Base.connection.next_sequence_value(Keyboard.sequence_name).should_not be_nil
41
+ end
42
+
43
+ it "should create sequence for default primary key" do
44
+ ActiveRecord::Base.connection.next_sequence_value(IdKeyboard.sequence_name).should_not be_nil
45
+ end
46
+ end
47
+
48
+ describe "sequence creation parameters" do
49
+
50
+ def create_test_employees_table(sequence_start_value = nil)
51
+ schema_define do
52
+ create_table :test_employees, sequence_start_value ? {:sequence_start_value => sequence_start_value} : {} do |t|
53
+ t.string :first_name
54
+ t.string :last_name
55
+ end
56
+ end
57
+ end
58
+
59
+ def save_default_sequence_start_value
60
+ @saved_sequence_start_value = ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value
61
+ end
62
+
63
+ def restore_default_sequence_start_value
64
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value = @saved_sequence_start_value
65
+ end
66
+
67
+ before(:each) do
68
+ save_default_sequence_start_value
69
+ end
70
+ after(:each) do
71
+ restore_default_sequence_start_value
72
+ schema_define do
73
+ drop_table :test_employees
74
+ end
75
+ Object.send(:remove_const, "TestEmployee")
76
+ end
77
+
78
+ it "should use default sequence start value 10000" do
79
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value.should == 10000
80
+
81
+ create_test_employees_table
82
+ class ::TestEmployee < ActiveRecord::Base; end
83
+
84
+ employee = TestEmployee.create!
85
+ employee.id.should == 10000
86
+ end
87
+
88
+ it "should use specified default sequence start value" do
89
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_sequence_start_value = 1
90
+
91
+ create_test_employees_table
92
+ class ::TestEmployee < ActiveRecord::Base; end
93
+
94
+ employee = TestEmployee.create!
95
+ employee.id.should == 1
96
+ end
97
+
98
+ it "should use sequence start value from table definition" do
99
+ create_test_employees_table(10)
100
+ class ::TestEmployee < ActiveRecord::Base; end
101
+
102
+ employee = TestEmployee.create!
103
+ employee.id.should == 10
104
+ end
105
+
106
+ it "should use sequence start value and other options from table definition" do
107
+ create_test_employees_table("100 NOCACHE INCREMENT BY 10")
108
+ class ::TestEmployee < ActiveRecord::Base; end
109
+
110
+ employee = TestEmployee.create!
111
+ employee.id.should == 100
112
+ employee = TestEmployee.create!
113
+ employee.id.should == 110
114
+ end
115
+
116
+ end
117
+
118
+ describe "create table with primary key trigger" do
119
+ def create_table_with_trigger(options = {})
120
+ options.merge! :primary_key_trigger => true, :force => true
121
+ schema_define do
122
+ create_table :test_employees, options do |t|
123
+ t.string :first_name
124
+ t.string :last_name
125
+ end
126
+ end
127
+ end
128
+
129
+ def create_table_and_separately_trigger(options = {})
130
+ options.merge! :force => true
131
+ schema_define do
132
+ create_table :test_employees, options do |t|
133
+ t.string :first_name
134
+ t.string :last_name
135
+ end
136
+ add_primary_key_trigger :test_employees, options
137
+ end
138
+ end
139
+
140
+ after(:all) do
141
+ seq_name = @sequence_name
142
+ schema_define do
143
+ drop_table :test_employees, (seq_name ? {:sequence_name => seq_name} : {})
144
+ end
145
+ Object.send(:remove_const, "TestEmployee")
146
+ @conn.clear_prefetch_primary_key
147
+ end
148
+
149
+ describe "with default primary key" do
150
+ before(:all) do
151
+ create_table_with_trigger
152
+ class ::TestEmployee < ActiveRecord::Base
153
+ end
154
+ end
155
+
156
+ it "should populate primary key using trigger" do
157
+ lambda do
158
+ @conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
159
+ end.should_not raise_error
160
+ end
161
+
162
+ it "should return new key value using connection insert method" do
163
+ insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, "id")
164
+ @conn.select_value("SELECT test_employees_seq.currval FROM dual").should == insert_id
165
+ end
166
+
167
+ it "should create new record for model" do
168
+ e = TestEmployee.create!(:first_name => 'Raimonds')
169
+ @conn.select_value("SELECT test_employees_seq.currval FROM dual").should == e.id
170
+ end
171
+ end
172
+
173
+ describe "with separate creation of primary key trigger" do
174
+ before(:all) do
175
+ create_table_and_separately_trigger
176
+ class ::TestEmployee < ActiveRecord::Base
177
+ end
178
+ end
179
+
180
+ it "should populate primary key using trigger" do
181
+ lambda do
182
+ @conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
183
+ end.should_not raise_error
184
+ end
185
+
186
+ it "should return new key value using connection insert method" do
187
+ insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, "id")
188
+ @conn.select_value("SELECT test_employees_seq.currval FROM dual").should == insert_id
189
+ end
190
+
191
+ it "should create new record for model" do
192
+ e = TestEmployee.create!(:first_name => 'Raimonds')
193
+ @conn.select_value("SELECT test_employees_seq.currval FROM dual").should == e.id
194
+ end
195
+ end
196
+
197
+ describe "with non-default primary key and non-default sequence name" do
198
+ before(:all) do
199
+ @primary_key = "employee_id"
200
+ @sequence_name = "test_employees_s"
201
+ create_table_with_trigger(:primary_key => @primary_key, :sequence_name => @sequence_name)
202
+ class ::TestEmployee < ActiveRecord::Base
203
+ set_primary_key "employee_id"
204
+ end
205
+ end
206
+
207
+ it "should populate primary key using trigger" do
208
+ lambda do
209
+ @conn.execute "INSERT INTO test_employees (first_name) VALUES ('Raimonds')"
210
+ end.should_not raise_error
211
+ end
212
+
213
+ it "should return new key value using connection insert method" do
214
+ insert_id = @conn.insert("INSERT INTO test_employees (first_name) VALUES ('Raimonds')", nil, @primary_key)
215
+ @conn.select_value("SELECT #{@sequence_name}.currval FROM dual").should == insert_id
216
+ end
217
+
218
+ it "should create new record for model with autogenerated sequence option" do
219
+ e = TestEmployee.create!(:first_name => 'Raimonds')
220
+ @conn.select_value("SELECT #{@sequence_name}.currval FROM dual").should == e.id
221
+ end
222
+ end
223
+
224
+ describe "with non-default sequence name and non-default trigger name" do
225
+ before(:all) do
226
+ @sequence_name = "test_employees_s"
227
+ create_table_with_trigger(:sequence_name => @sequence_name, :trigger_name => "test_employees_t1")
228
+ class ::TestEmployee < ActiveRecord::Base
229
+ set_sequence_name :autogenerated
230
+ end
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, "id")
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
+ end
251
+
252
+ describe "table and column comments" do
253
+
254
+ def create_test_employees_table(table_comment=nil, column_comments={})
255
+ schema_define do
256
+ create_table :test_employees, :comment => table_comment do |t|
257
+ t.string :first_name, :comment => column_comments[:first_name]
258
+ t.string :last_name, :comment => column_comments[:last_name]
259
+ end
260
+ end
261
+ end
262
+
263
+ after(:each) do
264
+ schema_define do
265
+ drop_table :test_employees
266
+ end
267
+ Object.send(:remove_const, "TestEmployee")
268
+ ActiveRecord::Base.table_name_prefix = nil
269
+ end
270
+
271
+ it "should create table with table comment" do
272
+ table_comment = "Test Employees"
273
+ create_test_employees_table(table_comment)
274
+ class ::TestEmployee < ActiveRecord::Base; end
275
+
276
+ @conn.table_comment("test_employees").should == table_comment
277
+ TestEmployee.table_comment.should == table_comment
278
+ end
279
+
280
+ it "should create table with columns comment" do
281
+ column_comments = {:first_name => "Given Name", :last_name => "Surname"}
282
+ create_test_employees_table(nil, column_comments)
283
+ class ::TestEmployee < ActiveRecord::Base; end
284
+
285
+ [:first_name, :last_name].each do |attr|
286
+ @conn.column_comment("test_employees", attr.to_s).should == column_comments[attr]
287
+ end
288
+ [:first_name, :last_name].each do |attr|
289
+ TestEmployee.columns_hash[attr.to_s].comment.should == column_comments[attr]
290
+ end
291
+ end
292
+
293
+ it "should create table with table and columns comment and custom table name prefix" do
294
+ ActiveRecord::Base.table_name_prefix = "xxx_"
295
+ table_comment = "Test Employees"
296
+ column_comments = {:first_name => "Given Name", :last_name => "Surname"}
297
+ create_test_employees_table(table_comment, column_comments)
298
+ class ::TestEmployee < ActiveRecord::Base; end
299
+
300
+ @conn.table_comment(TestEmployee.table_name).should == table_comment
301
+ TestEmployee.table_comment.should == table_comment
302
+ [:first_name, :last_name].each do |attr|
303
+ @conn.column_comment(TestEmployee.table_name, attr.to_s).should == column_comments[attr]
304
+ end
305
+ [:first_name, :last_name].each do |attr|
306
+ TestEmployee.columns_hash[attr.to_s].comment.should == column_comments[attr]
307
+ end
308
+ end
309
+
310
+ end
311
+
312
+ describe "create triggers" do
313
+
314
+ before(:all) do
315
+ schema_define do
316
+ create_table :test_employees do |t|
317
+ t.string :first_name
318
+ t.string :last_name
319
+ end
320
+ end
321
+ class ::TestEmployee < ActiveRecord::Base; end
322
+ end
323
+
324
+ after(:all) do
325
+ schema_define do
326
+ drop_table :test_employees
327
+ end
328
+ Object.send(:remove_const, "TestEmployee")
329
+ end
330
+
331
+ it "should create table trigger with :new reference" do
332
+ lambda do
333
+ @conn.execute <<-SQL
334
+ CREATE OR REPLACE TRIGGER test_employees_pkt
335
+ BEFORE INSERT ON test_employees FOR EACH ROW
336
+ BEGIN
337
+ IF inserting THEN
338
+ IF :new.id IS NULL THEN
339
+ SELECT test_employees_seq.NEXTVAL INTO :new.id FROM dual;
340
+ END IF;
341
+ END IF;
342
+ END;
343
+ SQL
344
+ end.should_not raise_error
345
+ end
346
+ end
347
+
348
+ describe "add index" do
349
+
350
+ it "should return default index name if it is not larger than 30 characters" do
351
+ @conn.index_name("employees", :column => "first_name").should == "index_employees_on_first_name"
352
+ end
353
+
354
+ it "should return shortened index name by removing 'index', 'on' and 'and' keywords" do
355
+ @conn.index_name("employees", :column => ["first_name", "email"]).should == "i_employees_first_name_email"
356
+ end
357
+
358
+ it "should return shortened index name by shortening table and column names" do
359
+ @conn.index_name("employees", :column => ["first_name", "last_name"]).should == "i_emp_fir_nam_las_nam"
360
+ end
361
+
362
+ it "should raise error if too large index name cannot be shortened" do
363
+ @conn.index_name("test_employees", :column => ["first_name", "middle_name", "last_name"]).should ==
364
+ 'i'+Digest::SHA1.hexdigest("index_test_employees_on_first_name_and_middle_name_and_last_name")[0,29]
365
+ end
366
+
367
+ end
368
+
369
+ describe "ignore options for LOB columns" do
370
+ after(:each) do
371
+ schema_define do
372
+ drop_table :test_posts
373
+ end
374
+ end
375
+
376
+ it "should ignore :limit option for :text column" do
377
+ lambda do
378
+ schema_define do
379
+ create_table :test_posts, :force => true do |t|
380
+ t.text :body, :limit => 10000
381
+ end
382
+ end
383
+ end.should_not raise_error
384
+ end
385
+
386
+ it "should ignore :limit option for :binary column" do
387
+ lambda do
388
+ schema_define do
389
+ create_table :test_posts, :force => true do |t|
390
+ t.binary :picture, :limit => 10000
391
+ end
392
+ end
393
+ end.should_not raise_error
394
+ end
395
+
396
+ end
397
+
398
+ describe "foreign key constraints" do
399
+ before(:each) do
400
+ schema_define do
401
+ create_table :test_posts, :force => true do |t|
402
+ t.string :title
403
+ end
404
+ create_table :test_comments, :force => true do |t|
405
+ t.string :body, :limit => 4000
406
+ t.references :test_post
407
+ t.integer :post_id
408
+ end
409
+ end
410
+ class ::TestPost < ActiveRecord::Base
411
+ has_many :test_comments
412
+ end
413
+ class ::TestComment < ActiveRecord::Base
414
+ belongs_to :test_post
415
+ end
416
+ end
417
+
418
+ after(:each) do
419
+ Object.send(:remove_const, "TestPost")
420
+ Object.send(:remove_const, "TestComment")
421
+ schema_define do
422
+ drop_table :test_comments rescue nil
423
+ drop_table :test_posts rescue nil
424
+ end
425
+ end
426
+
427
+ it "should add foreign key" do
428
+ schema_define do
429
+ add_foreign_key :test_comments, :test_posts
430
+ end
431
+ lambda do
432
+ TestComment.create(:body => "test", :test_post_id => 1)
433
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291.*\.TEST_COMMENTS_TEST_POST_ID_FK/}
434
+ end
435
+
436
+ it "should add foreign key with name" do
437
+ schema_define do
438
+ add_foreign_key :test_comments, :test_posts, :name => "comments_posts_fk"
439
+ end
440
+ lambda do
441
+ TestComment.create(:body => "test", :test_post_id => 1)
442
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291.*\.COMMENTS_POSTS_FK/}
443
+ end
444
+
445
+ it "should add foreign key with long name which is shortened" do
446
+ schema_define do
447
+ add_foreign_key :test_comments, :test_posts, :name => "test_comments_test_post_id_foreign_key"
448
+ end
449
+ lambda do
450
+ TestComment.create(:body => "test", :test_post_id => 1)
451
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291.*\.TES_COM_TES_POS_ID_FOR_KEY/}
452
+ end
453
+
454
+ it "should add foreign key with very long name which is shortened" do
455
+ schema_define do
456
+ add_foreign_key :test_comments, :test_posts, :name => "long_prefix_test_comments_test_post_id_foreign_key"
457
+ end
458
+ lambda do
459
+ TestComment.create(:body => "test", :test_post_id => 1)
460
+ end.should raise_error() {|e| e.message.should =~
461
+ /ORA-02291.*\.C#{Digest::SHA1.hexdigest("long_prefix_test_comments_test_post_id_foreign_key")[0,29].upcase}/}
462
+ end
463
+
464
+ it "should add foreign key with column" do
465
+ schema_define do
466
+ add_foreign_key :test_comments, :test_posts, :column => "post_id"
467
+ end
468
+ lambda do
469
+ TestComment.create(:body => "test", :post_id => 1)
470
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291.*\.TEST_COMMENTS_POST_ID_FK/}
471
+ end
472
+
473
+ it "should add foreign key with delete dependency" do
474
+ schema_define do
475
+ add_foreign_key :test_comments, :test_posts, :dependent => :delete
476
+ end
477
+ p = TestPost.create(:title => "test")
478
+ c = TestComment.create(:body => "test", :test_post => p)
479
+ TestPost.delete(p.id)
480
+ TestComment.find_by_id(c.id).should be_nil
481
+ end
482
+
483
+ it "should add foreign key with nullify dependency" do
484
+ schema_define do
485
+ add_foreign_key :test_comments, :test_posts, :dependent => :nullify
486
+ end
487
+ p = TestPost.create(:title => "test")
488
+ c = TestComment.create(:body => "test", :test_post => p)
489
+ TestPost.delete(p.id)
490
+ TestComment.find_by_id(c.id).test_post_id.should be_nil
491
+ end
492
+
493
+ it "should remove foreign key by table name" do
494
+ schema_define do
495
+ add_foreign_key :test_comments, :test_posts
496
+ remove_foreign_key :test_comments, :test_posts
497
+ end
498
+ lambda do
499
+ TestComment.create(:body => "test", :test_post_id => 1)
500
+ end.should_not raise_error
501
+ end
502
+
503
+ it "should remove foreign key by constraint name" do
504
+ schema_define do
505
+ add_foreign_key :test_comments, :test_posts, :name => "comments_posts_fk"
506
+ remove_foreign_key :test_comments, :name => "comments_posts_fk"
507
+ end
508
+ lambda do
509
+ TestComment.create(:body => "test", :test_post_id => 1)
510
+ end.should_not raise_error
511
+ end
512
+
513
+ it "should remove foreign key by column name" do
514
+ schema_define do
515
+ add_foreign_key :test_comments, :test_posts
516
+ remove_foreign_key :test_comments, :column => "test_post_id"
517
+ end
518
+ lambda do
519
+ TestComment.create(:body => "test", :test_post_id => 1)
520
+ end.should_not raise_error
521
+ end
522
+
523
+ end
524
+
525
+ describe "foreign key in table definition" do
526
+ before(:each) do
527
+ schema_define do
528
+ create_table :test_posts, :force => true do |t|
529
+ t.string :title
530
+ end
531
+ end
532
+ class ::TestPost < ActiveRecord::Base
533
+ has_many :test_comments
534
+ end
535
+ class ::TestComment < ActiveRecord::Base
536
+ belongs_to :test_post
537
+ end
538
+ end
539
+
540
+ after(:each) do
541
+ Object.send(:remove_const, "TestPost")
542
+ Object.send(:remove_const, "TestComment")
543
+ schema_define do
544
+ drop_table :test_comments rescue nil
545
+ drop_table :test_posts rescue nil
546
+ end
547
+ end
548
+
549
+ it "should add foreign key in create_table" do
550
+ schema_define do
551
+ create_table :test_comments, :force => true do |t|
552
+ t.string :body, :limit => 4000
553
+ t.references :test_post
554
+ t.foreign_key :test_posts
555
+ end
556
+ end
557
+ lambda do
558
+ TestComment.create(:body => "test", :test_post_id => 1)
559
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291/}
560
+ end
561
+
562
+ it "should add foreign key in create_table references" do
563
+ schema_define do
564
+ create_table :test_comments, :force => true do |t|
565
+ t.string :body, :limit => 4000
566
+ t.references :test_post, :foreign_key => true
567
+ end
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/}
572
+ end
573
+
574
+ it "should add foreign key in change_table" do
575
+ return pending("Not in this ActiveRecord version") unless ENV['RAILS_GEM_VERSION'] >= '2.1'
576
+ schema_define do
577
+ create_table :test_comments, :force => true do |t|
578
+ t.string :body, :limit => 4000
579
+ t.references :test_post
580
+ end
581
+ change_table :test_comments do |t|
582
+ t.foreign_key :test_posts
583
+ end
584
+ end
585
+ lambda do
586
+ TestComment.create(:body => "test", :test_post_id => 1)
587
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291/}
588
+ end
589
+
590
+ it "should add foreign key in change_table references" do
591
+ return pending("Not in this ActiveRecord version") unless ENV['RAILS_GEM_VERSION'] >= '2.1'
592
+ schema_define do
593
+ create_table :test_comments, :force => true do |t|
594
+ t.string :body, :limit => 4000
595
+ end
596
+ change_table :test_comments do |t|
597
+ t.references :test_post, :foreign_key => true
598
+ end
599
+ end
600
+ lambda do
601
+ TestComment.create(:body => "test", :test_post_id => 1)
602
+ end.should raise_error() {|e| e.message.should =~ /ORA-02291/}
603
+ end
604
+
605
+ it "should remove foreign key by table name" do
606
+ return pending("Not in this ActiveRecord version") unless ENV['RAILS_GEM_VERSION'] >= '2.1'
607
+ schema_define do
608
+ create_table :test_comments, :force => true do |t|
609
+ t.string :body, :limit => 4000
610
+ t.references :test_post
611
+ end
612
+ change_table :test_comments do |t|
613
+ t.foreign_key :test_posts
614
+ end
615
+ change_table :test_comments do |t|
616
+ t.remove_foreign_key :test_posts
617
+ end
618
+ end
619
+ lambda do
620
+ TestComment.create(:body => "test", :test_post_id => 1)
621
+ end.should_not raise_error
622
+ end
623
+
624
+ end
625
+
626
+ describe "disable referential integrity" do
627
+ before(:each) do
628
+ schema_define do
629
+ create_table :test_posts, :force => true do |t|
630
+ t.string :title
631
+ end
632
+ create_table :test_comments, :force => true do |t|
633
+ t.string :body, :limit => 4000
634
+ t.references :test_post, :foreign_key => true
635
+ end
636
+ end
637
+ end
638
+
639
+ after(:each) do
640
+ schema_define do
641
+ drop_table :test_comments rescue nil
642
+ drop_table :test_posts rescue nil
643
+ end
644
+ end
645
+
646
+ it "should disable all foreign keys" do
647
+ lambda do
648
+ @conn.execute "INSERT INTO test_comments (id, body, test_post_id) VALUES (1, 'test', 1)"
649
+ end.should raise_error
650
+ @conn.disable_referential_integrity do
651
+ lambda do
652
+ @conn.execute "INSERT INTO test_comments (id, body, test_post_id) VALUES (2, 'test', 2)"
653
+ @conn.execute "INSERT INTO test_posts (id, title) VALUES (2, 'test')"
654
+ end.should_not raise_error
655
+ end
656
+ lambda do
657
+ @conn.execute "INSERT INTO test_comments (id, body, test_post_id) VALUES (3, 'test', 3)"
658
+ end.should raise_error
659
+ end
660
+
661
+ end
662
+
663
+ describe 'virtual columns' do
664
+ before(:all) do
665
+ schema_define do
666
+ @expr = "( numerator/NULLIF(denominator,0) )*100"
667
+ create_table :test_fractions, :force => true do |t|
668
+ t.integer :numerator, :default=>0
669
+ t.integer :denominator, :default=>0
670
+ t.virtual :percent, :default=>@expr
671
+ end
672
+ end
673
+ end
674
+ before(:each) do
675
+ class ::TestFraction < ActiveRecord::Base
676
+ set_table_name "test_fractions"
677
+
678
+ end
679
+ end
680
+
681
+ after(:all) do
682
+ schema_define do
683
+ drop_table :test_fractions
684
+ end
685
+ end
686
+
687
+ it 'should include virtual columns' do
688
+ tf = TestFraction.columns.detect{|c| c.virtual?}
689
+ tf.should_not be nil
690
+ tf.name.should == "percent"
691
+ tf.virtual?.should be true
692
+ lambda do
693
+ tf = TestFraction.create!(:numerator=>20, :denominator=>100)
694
+ tf.reload
695
+ end.should_not raise_error
696
+ tf.percent.to_i.should == 20
697
+ end
698
+
699
+ end
700
+
701
+ describe "synonyms" do
702
+ before(:all) do
703
+ @db_link = "db_link"
704
+ @username = @db_link_username = CONNECTION_PARAMS[:username]
705
+ @db_link_password = CONNECTION_PARAMS[:password]
706
+ @db_link_database = CONNECTION_PARAMS[:database]
707
+ @conn.execute "DROP DATABASE LINK #{@db_link}" rescue nil
708
+ @conn.execute "CREATE DATABASE LINK #{@db_link} CONNECT TO #{@db_link_username} IDENTIFIED BY #{@db_link_password} USING '#{@db_link_database}'"
709
+ schema_define do
710
+ create_table :test_posts, :force => true do |t|
711
+ t.string :title
712
+ end
713
+ end
714
+ end
715
+
716
+ after(:all) do
717
+ schema_define do
718
+ drop_table :test_posts
719
+ end
720
+ @conn.execute "DROP DATABASE LINK #{@db_link}" rescue nil
721
+ end
722
+
723
+ before(:each) do
724
+ class ::TestPost < ActiveRecord::Base
725
+ set_table_name "synonym_to_posts"
726
+ end
727
+ end
728
+
729
+ after(:each) do
730
+ Object.send(:remove_const, "TestPost")
731
+ schema_define do
732
+ remove_synonym :synonym_to_posts
733
+ remove_synonym :synonym_to_posts_seq
734
+ end
735
+ end
736
+
737
+ it "should create synonym to table and sequence" do
738
+ schema_name = @username
739
+ schema_define do
740
+ add_synonym :synonym_to_posts, "#{schema_name}.test_posts", :force => true
741
+ add_synonym :synonym_to_posts_seq, "#{schema_name}.test_posts_seq", :force => true
742
+ end
743
+ lambda do
744
+ TestPost.create(:title => "test")
745
+ end.should_not raise_error
746
+ end
747
+
748
+ it "should create synonym to table over database link" do
749
+ db_link = @db_link
750
+ schema_define do
751
+ add_synonym :synonym_to_posts, "test_posts@#{db_link}", :force => true
752
+ add_synonym :synonym_to_posts_seq, "test_posts_seq@#{db_link}", :force => true
753
+ end
754
+ lambda do
755
+ TestPost.create(:title => "test")
756
+ end.should_not raise_error
757
+ end
758
+
759
+ end
760
+
761
+ end