activerecord-oracle_enhanced-adapter 1.4.3 → 5.2.0

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