activerecord-oracle_enhanced-adapter 1.4.3 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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