unclebilly-activerecord-oracle_enhanced-adapter 1.2.4

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