activerecord-oracle_enhanced-adapter 8.1.0-java

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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1971 -0
  3. data/License.txt +20 -0
  4. data/README.md +947 -0
  5. data/VERSION +1 -0
  6. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +7 -0
  7. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +24 -0
  8. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +137 -0
  9. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +359 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +47 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +325 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +63 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +71 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +629 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +38 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +57 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +465 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +44 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +195 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +186 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +95 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +99 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +197 -0
  24. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +739 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +394 -0
  26. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +34 -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 +886 -0
  29. data/lib/active_record/type/oracle_enhanced/boolean.rb +19 -0
  30. data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
  31. data/lib/active_record/type/oracle_enhanced/integer.rb +14 -0
  32. data/lib/active_record/type/oracle_enhanced/json.rb +10 -0
  33. data/lib/active_record/type/oracle_enhanced/national_character_string.rb +26 -0
  34. data/lib/active_record/type/oracle_enhanced/national_character_text.rb +36 -0
  35. data/lib/active_record/type/oracle_enhanced/raw.rb +25 -0
  36. data/lib/active_record/type/oracle_enhanced/string.rb +29 -0
  37. data/lib/active_record/type/oracle_enhanced/text.rb +32 -0
  38. data/lib/active_record/type/oracle_enhanced/timestampltz.rb +25 -0
  39. data/lib/active_record/type/oracle_enhanced/timestamptz.rb +25 -0
  40. data/lib/activerecord-oracle_enhanced-adapter.rb +25 -0
  41. data/lib/arel/visitors/oracle.rb +216 -0
  42. data/lib/arel/visitors/oracle12.rb +121 -0
  43. data/lib/arel/visitors/oracle_common.rb +51 -0
  44. data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +24 -0
  45. data/spec/active_record/connection_adapters/oracle_enhanced/compatibility_spec.rb +40 -0
  46. data/spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb +84 -0
  47. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +589 -0
  48. data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +431 -0
  49. data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +122 -0
  50. data/spec/active_record/connection_adapters/oracle_enhanced/dbconsole_spec.rb +63 -0
  51. data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +69 -0
  52. data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +362 -0
  53. data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +181 -0
  54. data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +492 -0
  55. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +1318 -0
  56. data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +485 -0
  57. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +815 -0
  58. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +230 -0
  59. data/spec/active_record/oracle_enhanced/type/binary_spec.rb +119 -0
  60. data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +206 -0
  61. data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +67 -0
  62. data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
  63. data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
  64. data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +141 -0
  65. data/spec/active_record/oracle_enhanced/type/float_spec.rb +48 -0
  66. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +101 -0
  67. data/spec/active_record/oracle_enhanced/type/json_spec.rb +56 -0
  68. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +55 -0
  69. data/spec/active_record/oracle_enhanced/type/national_character_text_spec.rb +230 -0
  70. data/spec/active_record/oracle_enhanced/type/raw_spec.rb +137 -0
  71. data/spec/active_record/oracle_enhanced/type/text_spec.rb +295 -0
  72. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +107 -0
  73. data/spec/spec_config.yaml.template +11 -0
  74. data/spec/spec_helper.rb +225 -0
  75. data/spec/support/alter_system_set_open_cursors.sql +1 -0
  76. data/spec/support/alter_system_user_password.sql +2 -0
  77. data/spec/support/create_oracle_enhanced_users.sql +31 -0
  78. metadata +181 -0
@@ -0,0 +1,815 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe "OracleEnhancedAdapter" do
4
+ include LoggerSpecHelper
5
+ include SchemaSpecHelper
6
+
7
+ before(:all) do
8
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
9
+ end
10
+
11
+ describe "cache table columns" do
12
+ before(:all) do
13
+ @conn = ActiveRecord::Base.connection
14
+ schema_define do
15
+ create_table :test_employees, force: true do |t|
16
+ t.string :first_name, limit: 20
17
+ t.string :last_name, limit: 25
18
+ if ActiveRecord::Base.connection.supports_virtual_columns?
19
+ t.virtual :full_name, as: "(first_name || ' ' || last_name)"
20
+ else
21
+ t.string :full_name, limit: 46
22
+ end
23
+ t.date :hire_date
24
+ end
25
+ end
26
+ schema_define do
27
+ create_table :test_employees_without_pk, id: false, force: true do |t|
28
+ t.string :first_name, limit: 20
29
+ t.string :last_name, limit: 25
30
+ t.date :hire_date
31
+ end
32
+ end
33
+ @column_names = ["id", "first_name", "last_name", "full_name", "hire_date"]
34
+ @column_sql_types = ["NUMBER(38)", "VARCHAR2(20)", "VARCHAR2(25)", "VARCHAR2(46)", "DATE"]
35
+ class ::TestEmployee < ActiveRecord::Base
36
+ end
37
+ # Another class using the same table
38
+ class ::TestEmployee2 < ActiveRecord::Base
39
+ self.table_name = "test_employees"
40
+ end
41
+ end
42
+
43
+ after(:all) do
44
+ @conn = ActiveRecord::Base.connection
45
+ Object.send(:remove_const, "TestEmployee")
46
+ Object.send(:remove_const, "TestEmployee2")
47
+ @conn.drop_table :test_employees, if_exists: true
48
+ @conn.drop_table :test_employees_without_pk, if_exists: true
49
+ ActiveRecord::Base.clear_cache!
50
+ end
51
+
52
+ before(:each) do
53
+ set_logger
54
+ @conn = ActiveRecord::Base.connection
55
+ end
56
+
57
+ after(:each) do
58
+ clear_logger
59
+ end
60
+
61
+ describe "without column caching" do
62
+ it "should identify virtual columns as such" do
63
+ skip "Not supported in this database version" unless @conn.supports_virtual_columns?
64
+ te = TestEmployee.connection.columns("test_employees").detect(&:virtual?)
65
+ expect(te.name).to eq("full_name")
66
+ end
67
+
68
+ it "should get columns from database at first time" do
69
+ @conn.clear_table_columns_cache(:test_employees)
70
+ expect(TestEmployee.connection.columns("test_employees").map(&:name)).to eq(@column_names)
71
+ expect(@logger.logged(:debug).last).to match(/select .* from all_tab_cols/im)
72
+ end
73
+
74
+ it "should not get columns from database at second time" do
75
+ TestEmployee.connection.columns("test_employees")
76
+ @logger.clear(:debug)
77
+ expect(TestEmployee.connection.columns("test_employees").map(&:name)).to eq(@column_names)
78
+ expect(@logger.logged(:debug).last).not_to match(/select .* from all_tab_cols/im)
79
+ end
80
+
81
+ it "should get primary key from database at first time" do
82
+ expect(TestEmployee.connection.pk_and_sequence_for("test_employees")).to eq(["id", "test_employees_seq"])
83
+ expect(@logger.logged(:debug).last).to match(/select .* from all_constraints/im)
84
+ end
85
+
86
+ it "should get primary key from database at second time without query" do
87
+ expect(TestEmployee.connection.pk_and_sequence_for("test_employees")).to eq(["id", "test_employees_seq"])
88
+ @logger.clear(:debug)
89
+ expect(TestEmployee.connection.pk_and_sequence_for("test_employees")).to eq(["id", "test_employees_seq"])
90
+ expect(@logger.logged(:debug).last).to match(/select .* from all_constraints/im)
91
+ end
92
+
93
+ it "should have correct sql types when 2 models are using the same table and AR query cache is enabled" do
94
+ @conn.cache do
95
+ expect(TestEmployee.columns.map(&:sql_type)).to eq(@column_sql_types)
96
+ expect(TestEmployee2.columns.map(&:sql_type)).to eq(@column_sql_types)
97
+ end
98
+ end
99
+
100
+ it "should get sequence value at next time" do
101
+ TestEmployee.create!
102
+ expect(@logger.logged(:debug).first).not_to match(/SELECT "TEST_EMPLOYEES_SEQ".NEXTVAL FROM dual/im)
103
+ @logger.clear(:debug)
104
+ TestEmployee.create!
105
+ expect(@logger.logged(:debug).first).to match(/SELECT "TEST_EMPLOYEES_SEQ".NEXTVAL FROM dual/im)
106
+ end
107
+ end
108
+ end
109
+
110
+ describe "session information" do
111
+ before(:all) do
112
+ @conn = ActiveRecord::Base.connection
113
+ end
114
+
115
+ it "should get current database name" do
116
+ # get database name if using //host:port/database connection string
117
+ database_name = CONNECTION_PARAMS[:database].split("/").last
118
+ expect(@conn.current_database.upcase).to eq(database_name.upcase)
119
+ end
120
+
121
+ it "should get current database session user" do
122
+ expect(@conn.current_user.upcase).to eq(CONNECTION_PARAMS[:username].upcase)
123
+ end
124
+ end
125
+
126
+ describe "temporary tables" do
127
+ before(:all) do
128
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[:table] = "UNUSED"
129
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces[:clob] = "UNUSED"
130
+ @conn = ActiveRecord::Base.connection
131
+ end
132
+
133
+ after(:all) do
134
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.default_tablespaces = {}
135
+ end
136
+
137
+ after(:each) do
138
+ @conn.drop_table :foos, if_exists: true
139
+ end
140
+
141
+ it "should create ok" do
142
+ @conn.create_table :foos, temporary: true, id: false do |t|
143
+ t.integer :id
144
+ t.text :bar
145
+ end
146
+ end
147
+ it "should show up as temporary" do
148
+ @conn.create_table :foos, temporary: true, id: false do |t|
149
+ t.integer :id
150
+ end
151
+ expect(@conn.temporary_table?("foos")).to be_truthy
152
+ end
153
+ end
154
+
155
+ describe "`has_many` assoc has `dependent: :delete_all` with `order`" do
156
+ before(:all) do
157
+ schema_define do
158
+ create_table :test_posts do |t|
159
+ t.string :title
160
+ end
161
+ create_table :test_comments do |t|
162
+ t.integer :test_post_id
163
+ t.string :description
164
+ end
165
+ add_index :test_comments, :test_post_id
166
+ end
167
+ class ::TestPost < ActiveRecord::Base
168
+ has_many :test_comments, -> { order(:id) }, dependent: :delete_all
169
+ end
170
+ class ::TestComment < ActiveRecord::Base
171
+ belongs_to :test_post
172
+ end
173
+ TestPost.transaction do
174
+ post = TestPost.create!(title: "Title")
175
+ TestComment.create!(test_post_id: post.id, description: "Description")
176
+ end
177
+ end
178
+
179
+ after(:all) do
180
+ schema_define do
181
+ drop_table :test_comments
182
+ drop_table :test_posts
183
+ end
184
+ Object.send(:remove_const, "TestPost")
185
+ Object.send(:remove_const, "TestComment")
186
+ ActiveRecord::Base.clear_cache!
187
+ end
188
+
189
+ it "should not occur `ActiveRecord::StatementInvalid: OCIError: ORA-00907: missing right parenthesis`" do
190
+ expect { TestPost.first.destroy }.not_to raise_error
191
+ end
192
+ end
193
+
194
+ describe "eager loading" do
195
+ before(:all) do
196
+ schema_define do
197
+ create_table :test_posts do |t|
198
+ t.string :title
199
+ end
200
+ create_table :test_comments do |t|
201
+ t.integer :test_post_id
202
+ t.string :description
203
+ end
204
+ add_index :test_comments, :test_post_id
205
+ end
206
+ class ::TestPost < ActiveRecord::Base
207
+ has_many :test_comments
208
+ end
209
+ class ::TestComment < ActiveRecord::Base
210
+ belongs_to :test_post
211
+ end
212
+ @ids = (1..1010).to_a
213
+ TestPost.transaction do
214
+ @ids.each do |id|
215
+ TestPost.create!(id: id, title: "Title #{id}")
216
+ TestComment.create!(test_post_id: id, description: "Description #{id}")
217
+ end
218
+ end
219
+ end
220
+
221
+ after(:all) do
222
+ schema_define do
223
+ drop_table :test_comments
224
+ drop_table :test_posts
225
+ end
226
+ Object.send(:remove_const, "TestPost")
227
+ Object.send(:remove_const, "TestComment")
228
+ ActiveRecord::Base.clear_cache!
229
+ end
230
+
231
+ it "should load included association with more than 1000 records" do
232
+ posts = TestPost.includes(:test_comments).to_a
233
+ expect(posts.size).to eq(@ids.size)
234
+ end
235
+ end
236
+
237
+ describe "lists" do
238
+ before(:all) do
239
+ schema_define do
240
+ create_table :test_posts do |t|
241
+ t.string :title
242
+ end
243
+ end
244
+ class ::TestPost < ActiveRecord::Base
245
+ has_many :test_comments
246
+ end
247
+ @ids = (1..1010).to_a
248
+ TestPost.transaction do
249
+ @ids.each do |id|
250
+ TestPost.create!(id: id, title: "Title #{id}")
251
+ end
252
+ end
253
+ end
254
+
255
+ after(:all) do
256
+ schema_define do
257
+ drop_table :test_posts
258
+ end
259
+ Object.send(:remove_const, "TestPost")
260
+ ActiveRecord::Base.clear_cache!
261
+ end
262
+
263
+ ##
264
+ # See this GitHub issue for an explanation of homogenous lists.
265
+ # https://github.com/rails/rails/commit/72fd0bae5948c1169411941aeea6fef4c58f34a9
266
+ it "should allow more than 1000 items in a list where the list is homogenous" do
267
+ posts = TestPost.where(id: @ids).to_a
268
+ expect(posts.size).to eq(@ids.size)
269
+ end
270
+
271
+ it "should allow more than 1000 items in a list where the list is non-homogenous" do
272
+ posts = TestPost.where(id: [*@ids, nil]).to_a
273
+ expect(posts.size).to eq(@ids.size)
274
+ end
275
+ end
276
+
277
+ describe "with statement pool" do
278
+ before(:all) do
279
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS.merge(statement_limit: 3))
280
+ @conn = ActiveRecord::Base.connection
281
+ schema_define do
282
+ drop_table :test_posts, if_exists: true
283
+ create_table :test_posts
284
+ end
285
+ class ::TestPost < ActiveRecord::Base
286
+ end
287
+ @statements = @conn.instance_variable_get(:@statements)
288
+ end
289
+
290
+ before(:each) do
291
+ @conn.clear_cache!
292
+ end
293
+
294
+ after(:all) do
295
+ schema_define do
296
+ drop_table :test_posts
297
+ end
298
+ Object.send(:remove_const, "TestPost")
299
+ ActiveRecord::Base.clear_cache!
300
+ end
301
+
302
+ it "should clear older cursors when statement limit is reached" do
303
+ binds = [ActiveRecord::Relation::QueryAttribute.new("id", 1, ActiveRecord::Type::OracleEnhanced::Integer.new)]
304
+ # free statement pool from dictionary selections to ensure next selects will increase statement pool
305
+ @statements.clear
306
+ expect {
307
+ 4.times do |i|
308
+ @conn.exec_query("SELECT * FROM test_posts WHERE #{i}=#{i} AND id = :id", "SQL", binds)
309
+ end
310
+ }.to change(@statements, :length).by(+3)
311
+ end
312
+
313
+ it "should cache UPDATE statements with bind variables" do
314
+ expect {
315
+ binds = [ActiveRecord::Relation::QueryAttribute.new("id", 1, ActiveRecord::Type::OracleEnhanced::Integer.new)]
316
+ @conn.exec_update("UPDATE test_posts SET id = :id", "SQL", binds)
317
+ }.to change(@statements, :length).by(+1)
318
+ end
319
+
320
+ it "should not cache UPDATE statements without bind variables" do
321
+ expect {
322
+ binds = []
323
+ @conn.exec_update("UPDATE test_posts SET id = 1", "SQL", binds)
324
+ }.not_to change(@statements, :length)
325
+ end
326
+ end
327
+
328
+ describe "database_exists?" do
329
+ it "should raise `NotImplementedError`" do
330
+ expect {
331
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.database_exists?(CONNECTION_PARAMS)
332
+ }.to raise_error(NotImplementedError)
333
+ end
334
+ end
335
+
336
+ describe "explain" do
337
+ before(:all) do
338
+ @conn = ActiveRecord::Base.connection
339
+ schema_define do
340
+ drop_table :test_posts, if_exists: true
341
+ create_table :test_posts
342
+ end
343
+ class ::TestPost < ActiveRecord::Base
344
+ end
345
+ end
346
+
347
+ after(:all) do
348
+ schema_define do
349
+ drop_table :test_posts
350
+ end
351
+ Object.send(:remove_const, "TestPost")
352
+ ActiveRecord::Base.clear_cache!
353
+ end
354
+
355
+ it "should explain query" do
356
+ explain = TestPost.where(id: 1).explain
357
+ expect(explain.inspect).to include("Cost")
358
+ expect(explain.inspect).to include("INDEX UNIQUE SCAN")
359
+ end
360
+
361
+ it "should explain query with binds" do
362
+ binds = [ActiveRecord::Relation::QueryAttribute.new("id", 1, ActiveRecord::Type::OracleEnhanced::Integer.new)]
363
+ explain = TestPost.where(id: binds).explain
364
+ expect(explain.inspect).to include("Cost")
365
+ expect(explain.inspect).to include("INDEX UNIQUE SCAN").or include("TABLE ACCESS FULL")
366
+ end
367
+ end
368
+
369
+ describe "using offset and limit" do
370
+ before(:all) do
371
+ @conn = ActiveRecord::Base.connection
372
+ schema_define do
373
+ create_table :test_employees, force: true do |t|
374
+ t.integer :sort_order
375
+ t.string :first_name, limit: 20
376
+ t.string :last_name, limit: 20
377
+ t.timestamps
378
+ end
379
+ end
380
+ @employee = Class.new(ActiveRecord::Base) do
381
+ self.table_name = :test_employees
382
+ end
383
+ @employee.create!(sort_order: 1, first_name: "Peter", last_name: "Parker")
384
+ @employee.create!(sort_order: 2, first_name: "Tony", last_name: "Stark")
385
+ @employee.create!(sort_order: 3, first_name: "Steven", last_name: "Rogers")
386
+ @employee.create!(sort_order: 4, first_name: "Bruce", last_name: "Banner")
387
+ @employee.create!(sort_order: 5, first_name: "Natasha", last_name: "Romanova")
388
+ end
389
+
390
+ after(:all) do
391
+ @conn.drop_table :test_employees, if_exists: true
392
+ end
393
+
394
+ after(:each) do
395
+ ActiveRecord::Base.clear_cache!
396
+ end
397
+
398
+ it "should return n records with limit(n)" do
399
+ expect(@employee.limit(3).to_a.size).to be(3)
400
+ end
401
+
402
+ it "should return less than n records with limit(n) if there exist less than n records" do
403
+ expect(@employee.limit(10).to_a.size).to be(5)
404
+ end
405
+
406
+ it "should return the records starting from offset n with offset(n)" do
407
+ expect(@employee.order(:sort_order).first.first_name).to eq("Peter")
408
+ expect(@employee.order(:sort_order).offset(0).first.first_name).to eq("Peter")
409
+ expect(@employee.order(:sort_order).offset(1).first.first_name).to eq("Tony")
410
+ expect(@employee.order(:sort_order).offset(4).first.first_name).to eq("Natasha")
411
+ end
412
+ end
413
+
414
+ describe "valid_type?" do
415
+ before(:all) do
416
+ @conn = ActiveRecord::Base.connection
417
+ schema_define do
418
+ create_table :test_employees, force: true do |t|
419
+ t.string :first_name, limit: 20
420
+ end
421
+ end
422
+ end
423
+
424
+ after(:all) do
425
+ @conn.drop_table :test_employees, if_exists: true
426
+ end
427
+
428
+ it "returns true when passed a valid type" do
429
+ column = @conn.columns("test_employees").find { |col| col.name == "first_name" }
430
+ expect(@conn.valid_type?(column.type)).to be true
431
+ end
432
+
433
+ it "returns false when passed an invalid type" do
434
+ expect(@conn.valid_type?(:foobar)).to be false
435
+ end
436
+ end
437
+
438
+ describe "serialized column" do
439
+ before(:all) do
440
+ schema_define do
441
+ create_table :test_serialized_columns do |t|
442
+ t.text :serialized
443
+ end
444
+ end
445
+ class ::TestSerializedColumn < ActiveRecord::Base
446
+ serialize :serialized, type: Array
447
+ end
448
+ end
449
+
450
+ after(:all) do
451
+ schema_define do
452
+ drop_table :test_serialized_columns
453
+ end
454
+ Object.send(:remove_const, "TestSerializedColumn")
455
+ ActiveRecord::Base.table_name_prefix = nil
456
+ ActiveRecord::Base.clear_cache!
457
+ end
458
+
459
+ before(:each) do
460
+ set_logger
461
+ end
462
+
463
+ after(:each) do
464
+ clear_logger
465
+ end
466
+
467
+ it "should serialize" do
468
+ new_value = "new_value"
469
+ serialized_column = TestSerializedColumn.new
470
+
471
+ expect(serialized_column.serialized).to eq([])
472
+ serialized_column.serialized << new_value
473
+ expect(serialized_column.serialized).to eq([new_value])
474
+ serialized_column.save
475
+ expect(serialized_column.save!).to be(true)
476
+
477
+ serialized_column.reload
478
+ expect(serialized_column.serialized).to eq([new_value])
479
+ serialized_column.serialized = []
480
+ expect(serialized_column.save!).to be(true)
481
+ end
482
+ end
483
+
484
+ describe "Binary lob column" do
485
+ before(:all) do
486
+ schema_define do
487
+ create_table :test_binary_columns do |t|
488
+ t.binary :attachment
489
+ end
490
+ end
491
+ class ::TestBinaryColumn < ActiveRecord::Base
492
+ end
493
+ end
494
+
495
+ after(:all) do
496
+ schema_define do
497
+ drop_table :test_binary_columns
498
+ end
499
+ Object.send(:remove_const, "TestBinaryColumn")
500
+ ActiveRecord::Base.table_name_prefix = nil
501
+ ActiveRecord::Base.clear_cache!
502
+ end
503
+
504
+ before(:each) do
505
+ set_logger
506
+ end
507
+
508
+ after(:each) do
509
+ clear_logger
510
+ end
511
+
512
+ it "should serialize with non UTF-8 data" do
513
+ binary_value = +"Hello \x93\xfa\x96\x7b"
514
+ binary_value.force_encoding "UTF-8"
515
+
516
+ binary_column_object = TestBinaryColumn.new
517
+ binary_column_object.attachment = binary_value
518
+
519
+ expect(binary_column_object.save!).to be(true)
520
+ end
521
+ end
522
+
523
+ describe "quoting" do
524
+ before(:all) do
525
+ schema_define do
526
+ create_table :test_logs, force: true do |t|
527
+ t.timestamp :send_time
528
+ end
529
+ end
530
+ class TestLog < ActiveRecord::Base
531
+ validates_uniqueness_of :send_time
532
+ end
533
+ end
534
+
535
+ after(:all) do
536
+ schema_define do
537
+ drop_table :test_logs
538
+ end
539
+ Object.send(:remove_const, "TestLog")
540
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
541
+ end
542
+
543
+ it "should create records including Time" do
544
+ TestLog.create! send_time: Time.now + 1.seconds
545
+ TestLog.create! send_time: Time.now + 2.seconds
546
+ expect(TestLog.count).to eq 2
547
+ end
548
+ end
549
+
550
+ describe "synonym_names" do
551
+ before(:all) do
552
+ schema_define do
553
+ create_table :test_comments, force: true do |t|
554
+ t.string :comment
555
+ end
556
+ add_synonym :synonym_comments, :test_comments
557
+ end
558
+ end
559
+
560
+ after(:all) do
561
+ schema_define do
562
+ drop_table :test_comments
563
+ remove_synonym :synonym_comments
564
+ end
565
+ ActiveRecord::Base.clear_cache! if ActiveRecord::Base.respond_to?(:"clear_cache!")
566
+ end
567
+
568
+ it "includes synonyms in data_source" do
569
+ conn = ActiveRecord::Base.connection
570
+ expect(conn).to be_data_source_exist("synonym_comments")
571
+ expect(conn.data_sources).to include("synonym_comments")
572
+ end
573
+ end
574
+
575
+ describe "dictionary selects with bind variables" do
576
+ before(:all) do
577
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
578
+ @conn = ActiveRecord::Base.connection
579
+ schema_define do
580
+ drop_table :test_posts, if_exists: true
581
+ create_table :test_posts
582
+
583
+ drop_table :users, if_exists: true
584
+ create_table :users, force: true do |t|
585
+ t.string :name
586
+ t.integer :group_id
587
+ end
588
+
589
+ drop_table :groups, if_exists: true
590
+ create_table :groups, force: true do |t|
591
+ t.string :name
592
+ end
593
+ end
594
+
595
+ class ::TestPost < ActiveRecord::Base
596
+ end
597
+
598
+ class User < ActiveRecord::Base
599
+ belongs_to :group
600
+ end
601
+
602
+ class Group < ActiveRecord::Base
603
+ has_one :user
604
+ end
605
+ end
606
+
607
+ before(:each) do
608
+ @conn.clear_cache!
609
+ set_logger
610
+ end
611
+
612
+ after(:each) do
613
+ clear_logger
614
+ end
615
+
616
+ after(:all) do
617
+ schema_define do
618
+ drop_table :test_posts
619
+ drop_table :users
620
+ drop_table :groups
621
+ end
622
+ Object.send(:remove_const, "TestPost")
623
+ ActiveRecord::Base.clear_cache!
624
+ end
625
+
626
+ it "should test table existence" do
627
+ expect(@conn.table_exists?("TEST_POSTS")).to be true
628
+ expect(@conn.table_exists?("NOT_EXISTING")).to be false
629
+ end
630
+
631
+ it "should return array from indexes with bind usage" do
632
+ expect(@conn.indexes("TEST_POSTS").class).to eq Array
633
+ expect(@logger.logged(:debug).last).to match(/:table_name/)
634
+ expect(@logger.logged(:debug).last).to match(/\["table_name", "TEST_POSTS"\]/)
635
+ end
636
+
637
+ it "should return content from columns witt bind usage" do
638
+ expect(@conn.columns("TEST_POSTS").length).to be > 0
639
+ expect(@logger.logged(:debug).last).to match(/:table_name/)
640
+ expect(@logger.logged(:debug).last).to match(/\["table_name", "TEST_POSTS"\]/)
641
+ end
642
+
643
+ it "should return pk and sequence from pk_and_sequence_for with bind usage" do
644
+ expect(@conn.pk_and_sequence_for("TEST_POSTS").length).to eq 2
645
+ expect(@logger.logged(:debug).last).to match(/\["table_name", "TEST_POSTS"\]/)
646
+ end
647
+
648
+ it "should return pk from primary_keys with bind usage" do
649
+ expect(@conn.primary_keys("TEST_POSTS")).to eq ["id"]
650
+ expect(@logger.logged(:debug).last).to match(/\["table_name", "TEST_POSTS"\]/)
651
+ end
652
+
653
+ it "should not raise missing IN/OUT parameter like issue 1678" do
654
+ # "to_sql" enforces unprepared_statement including dictionary access SQLs
655
+ expect { User.joins(:group).to_sql }.not_to raise_exception
656
+ end
657
+
658
+ it "should return false from temporary_table? with bind usage" do
659
+ expect(@conn.temporary_table?("TEST_POSTS")).to be false
660
+ expect(@logger.logged(:debug).last).to match(/:table_name/)
661
+ expect(@logger.logged(:debug).last).to match(/\["table_name", "TEST_POSTS"\]/)
662
+ end
663
+ end
664
+
665
+ describe "Transaction" do
666
+ before(:all) do
667
+ schema_define do
668
+ create_table :test_posts do |t|
669
+ t.string :title
670
+ end
671
+ end
672
+ class ::TestPost < ActiveRecord::Base
673
+ end
674
+ Thread.report_on_exception, @original_report_on_exception = false, Thread.report_on_exception
675
+ end
676
+
677
+ it "Raises Deadlocked when a deadlock is encountered" do
678
+ skip "Skip temporary due to #1599" if ActiveRecord::Base.connection.supports_fetch_first_n_rows_and_offset?
679
+ expect {
680
+ barrier = Concurrent::CyclicBarrier.new(2)
681
+
682
+ t1 = TestPost.create(title: "one")
683
+ t2 = TestPost.create(title: "two")
684
+
685
+ thread = Thread.new do
686
+ TestPost.transaction do
687
+ t1.lock!
688
+ barrier.wait
689
+ t2.update(title: "one")
690
+ end
691
+ end
692
+
693
+ begin
694
+ TestPost.transaction do
695
+ t2.lock!
696
+ barrier.wait
697
+ t1.update(title: "two")
698
+ end
699
+ ensure
700
+ thread.join
701
+ end
702
+ }.to raise_error(ActiveRecord::Deadlocked)
703
+ end
704
+
705
+ after(:all) do
706
+ schema_define do
707
+ drop_table :test_posts
708
+ end
709
+ Object.send(:remove_const, "TestPost") rescue nil
710
+ ActiveRecord::Base.clear_cache!
711
+ Thread.report_on_exception = @original_report_on_exception
712
+ end
713
+ end
714
+
715
+ describe "Sequence" do
716
+ before(:all) do
717
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
718
+ @conn = ActiveRecord::Base.connection
719
+ schema_define do
720
+ create_table :table_with_name_thats_just_ok,
721
+ sequence_name: "suitably_short_seq", force: true do |t|
722
+ t.column :foo, :string, null: false
723
+ end
724
+ end
725
+ end
726
+
727
+ after(:all) do
728
+ schema_define do
729
+ drop_table :table_with_name_thats_just_ok,
730
+ sequence_name: "suitably_short_seq" rescue nil
731
+ end
732
+ end
733
+
734
+ it "should create table with custom sequence name" do
735
+ expect(@conn.select_value("select suitably_short_seq.nextval from dual")).to eq(1)
736
+ end
737
+ end
738
+
739
+ describe "Hints" do
740
+ before(:all) do
741
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
742
+ @conn = ActiveRecord::Base.connection
743
+ schema_define do
744
+ drop_table :test_posts, if_exists: true
745
+ create_table :test_posts
746
+ end
747
+ class ::TestPost < ActiveRecord::Base
748
+ end
749
+ end
750
+
751
+ before(:each) do
752
+ @conn.clear_cache!
753
+ set_logger
754
+ end
755
+
756
+ after(:each) do
757
+ clear_logger
758
+ end
759
+
760
+ after(:all) do
761
+ schema_define do
762
+ drop_table :test_posts
763
+ end
764
+ Object.send(:remove_const, "TestPost")
765
+ ActiveRecord::Base.clear_cache!
766
+ end
767
+
768
+ it "should explain considers hints" do
769
+ post = TestPost.optimizer_hints("FULL (\"TEST_POSTS\")")
770
+ post = post.where(id: 1)
771
+ expect(post.explain.inspect).to include("| TABLE ACCESS FULL| TEST_POSTS |")
772
+ end
773
+
774
+ it "should explain considers hints with /*+ */" do
775
+ post = TestPost.optimizer_hints("/*+ FULL (\"TEST_POSTS\") */")
776
+ post = post.where(id: 1)
777
+ expect(post.explain.inspect).to include("| TABLE ACCESS FULL| TEST_POSTS |")
778
+ end
779
+ end
780
+
781
+ describe "homogeneous in" do
782
+ before(:all) do
783
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
784
+ @conn = ActiveRecord::Base.connection
785
+ schema_define do
786
+ create_table :test_posts, force: true
787
+ create_table :test_comments, force: true do |t|
788
+ t.integer :test_post_id
789
+ end
790
+ end
791
+ class ::TestPost < ActiveRecord::Base
792
+ has_many :test_comments
793
+ end
794
+ class ::TestComment < ActiveRecord::Base
795
+ belongs_to :test_post
796
+ end
797
+ end
798
+
799
+ after(:all) do
800
+ schema_define do
801
+ drop_table :test_posts, if_exists: true
802
+ drop_table :test_comments, if_exists: true
803
+ end
804
+ Object.send(:remove_const, "TestPost")
805
+ Object.send(:remove_const, "TestComment")
806
+ ActiveRecord::Base.clear_cache!
807
+ end
808
+
809
+ it "should not raise undefined method length" do
810
+ post = TestPost.create!
811
+ post.test_comments << TestComment.create!
812
+ expect(TestComment.where(test_post_id: TestPost.select(:id)).size).to eq(1)
813
+ end
814
+ end
815
+ end