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