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,589 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe "OracleEnhancedAdapter establish connection" do
4
+ it "should connect to database" do
5
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
6
+ expect(ActiveRecord::Base.connection).not_to be_nil
7
+ expect(ActiveRecord::Base.connection.class).to eq(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
8
+ end
9
+
10
+ it "should connect to database as SYSDBA" do
11
+ ActiveRecord::Base.establish_connection(SYS_CONNECTION_PARAMS)
12
+ expect(ActiveRecord::Base.connection).not_to be_nil
13
+ expect(ActiveRecord::Base.connection.class).to eq(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
14
+ end
15
+
16
+ it "should be active after connection to database" do
17
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
18
+ expect(ActiveRecord::Base.connection).to be_active
19
+ end
20
+
21
+ it "should not be active after disconnection to database" do
22
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
23
+ ActiveRecord::Base.connection.disconnect!
24
+ expect(ActiveRecord::Base.connection).not_to be_active
25
+ end
26
+
27
+ it "should be active after reconnection to database" do
28
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
29
+ ActiveRecord::Base.connection.reconnect!
30
+ expect(ActiveRecord::Base.connection).to be_active
31
+ end
32
+
33
+ it "should be active after reconnection to database with restore_transactions: true" do
34
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
35
+ ActiveRecord::Base.connection.reconnect!(restore_transactions: true)
36
+ expect(ActiveRecord::Base.connection).to be_active
37
+ end
38
+
39
+ it "should use database default cursor_sharing parameter value force by default" do
40
+ # Use `SYSTEM_CONNECTION_PARAMS` to query v$parameter
41
+ ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS)
42
+ expect(ActiveRecord::Base.connection.select_value("select value from v$parameter where name = 'cursor_sharing'")).to eq("FORCE")
43
+ end
44
+
45
+ it "should use modified cursor_sharing value exact" do
46
+ ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS.merge(cursor_sharing: :exact))
47
+ expect(ActiveRecord::Base.connection.select_value("select value from v$parameter where name = 'cursor_sharing'")).to eq("EXACT")
48
+ end
49
+
50
+ it "should not use JDBC statement caching" do
51
+ if ORACLE_ENHANCED_CONNECTION == :jdbc
52
+ ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS)
53
+ expect(ActiveRecord::Base.connection.raw_connection.getImplicitCachingEnabled).to be(false)
54
+ expect(ActiveRecord::Base.connection.raw_connection.getStatementCacheSize).to eq(-1)
55
+ end
56
+ end
57
+
58
+ it "should use JDBC statement caching" do
59
+ if ORACLE_ENHANCED_CONNECTION == :jdbc
60
+ ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS.merge(jdbc_statement_cache_size: 100))
61
+ expect(ActiveRecord::Base.connection.raw_connection.getImplicitCachingEnabled).to be(true)
62
+ expect(ActiveRecord::Base.connection.raw_connection.getStatementCacheSize).to eq(100)
63
+ # else: don't raise error if OCI connection has parameter "jdbc_statement_cache_size", still ignore it
64
+ end
65
+ end
66
+
67
+ it "should not encrypt JDBC network connection" do
68
+ skip "Oracle 11g XE does not support native network encryption" if ENV["DATABASE_VERSION"] == "11.2.0.2"
69
+ if ORACLE_ENHANCED_CONNECTION == :jdbc
70
+ ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS.merge(jdbc_connect_properties: { "oracle.net.encryption_client" => "REJECTED" }))
71
+ conn = ActiveRecord::Base.connection.send(:_connection)
72
+ expect(conn.select("SELECT COUNT(*) Records FROM v$Session_Connect_Info WHERE SID=SYS_CONTEXT('USERENV', 'SID') AND Network_Service_Banner LIKE '%Encryption service adapter%'")).to eq([{ "records" => 0 }])
73
+ end
74
+ end
75
+
76
+ it "should encrypt JDBC network connection" do
77
+ skip "Oracle 11g XE does not support native network encryption" if ENV["DATABASE_VERSION"] == "11.2.0.2"
78
+ if ORACLE_ENHANCED_CONNECTION == :jdbc
79
+ ActiveRecord::Base.establish_connection(SYSTEM_CONNECTION_PARAMS.merge(jdbc_connect_properties: { "oracle.net.encryption_client" => "REQUESTED" }))
80
+ conn = ActiveRecord::Base.connection.send(:_connection)
81
+ expect(conn.select("SELECT COUNT(*) Records FROM v$Session_Connect_Info WHERE SID=SYS_CONTEXT('USERENV', 'SID') AND Network_Service_Banner LIKE '%Encryption service adapter%'")).to eq([{ "records" => 1 }])
82
+ end
83
+ end
84
+
85
+ it "should connect to database using service_name" do
86
+ ActiveRecord::Base.establish_connection(SERVICE_NAME_CONNECTION_PARAMS)
87
+ expect(ActiveRecord::Base.connection).not_to be_nil
88
+ expect(ActiveRecord::Base.connection.class).to eq(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
89
+ end
90
+ end
91
+
92
+ describe "OracleEnhancedConnection" do
93
+ describe "create connection" do
94
+ before(:all) do
95
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
96
+ end
97
+
98
+ before(:each) do
99
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS) unless @conn.active?
100
+ end
101
+
102
+ it "should create new connection" do
103
+ expect(@conn).to be_active
104
+ end
105
+
106
+ it "should ping active connection" do
107
+ expect(@conn.ping).to be_truthy
108
+ end
109
+
110
+ it "should not ping inactive connection" do
111
+ @conn.logoff
112
+ expect { @conn.ping }.to raise_error(ActiveRecord::ConnectionAdapters::OracleEnhanced::ConnectionException)
113
+ end
114
+
115
+ it "should reset active connection" do
116
+ @conn.reset!
117
+ expect(@conn).to be_active
118
+ end
119
+
120
+ it "should be in autocommit mode after connection" do
121
+ expect(@conn).to be_autocommit
122
+ end
123
+
124
+ it "should raise ArgumentError when JDBC exec is called with bindvars" do
125
+ skip unless ORACLE_ENHANCED_CONNECTION == :jdbc
126
+ expect {
127
+ @conn.exec("SELECT ? FROM dual", 1)
128
+ }.to raise_error(ArgumentError, /JDBC exec does not support bindvars/)
129
+ end
130
+ end
131
+
132
+ describe "create connection with schema option" do
133
+ it "should create new connection" do
134
+ ActiveRecord::Base.establish_connection(CONNECTION_WITH_SCHEMA_PARAMS)
135
+ expect(ActiveRecord::Base.connection).to be_active
136
+ end
137
+
138
+ it "should switch to specified schema" do
139
+ ActiveRecord::Base.establish_connection(CONNECTION_WITH_SCHEMA_PARAMS)
140
+ expect(ActiveRecord::Base.connection.current_schema).to eq(CONNECTION_WITH_SCHEMA_PARAMS[:schema].upcase)
141
+ expect(ActiveRecord::Base.connection.current_user).to eq(CONNECTION_WITH_SCHEMA_PARAMS[:username].upcase)
142
+ end
143
+
144
+ it "should switch to specified schema after reset" do
145
+ ActiveRecord::Base.connection.reset!
146
+ expect(ActiveRecord::Base.connection.current_schema).to eq(CONNECTION_WITH_SCHEMA_PARAMS[:schema].upcase)
147
+ end
148
+ end
149
+
150
+ describe "create connection with NLS parameters" do
151
+ after do
152
+ ENV["NLS_TERRITORY"] = nil
153
+ end
154
+
155
+ it "should use NLS_TERRITORY environment variable" do
156
+ ENV["NLS_TERRITORY"] = "JAPAN"
157
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
158
+ expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_TERRITORY') as value from dual")).to eq([{ "value" => "JAPAN" }])
159
+ end
160
+
161
+ it "should use configuration value and ignore NLS_TERRITORY environment variable" do
162
+ ENV["NLS_TERRITORY"] = "AMERICA"
163
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS.merge(nls_territory: "INDONESIA"))
164
+ expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_TERRITORY') as value from dual")).to eq([{ "value" => "INDONESIA" }])
165
+ end
166
+ end
167
+
168
+ describe "Fixed NLS parameters" do
169
+ after do
170
+ ENV["NLS_DATE_FORMAT"] = nil
171
+ end
172
+
173
+ it "should ignore NLS_DATE_FORMAT environment variable" do
174
+ ENV["NLS_DATE_FORMAT"] = "YYYY-MM-DD"
175
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
176
+ expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_DATE_FORMAT') as value from dual")).to eq([{ "value" => "YYYY-MM-DD HH24:MI:SS" }])
177
+ end
178
+
179
+ it "should ignore NLS_DATE_FORMAT configuration value" do
180
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS.merge(nls_date_format: "YYYY-MM-DD HH24:MI"))
181
+ expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_DATE_FORMAT') as value from dual")).to eq([{ "value" => "YYYY-MM-DD HH24:MI:SS" }])
182
+ end
183
+
184
+ it "should use default value when NLS_DATE_FORMAT environment variable is not set" do
185
+ ENV["NLS_DATE_FORMAT"] = nil
186
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
187
+ default = ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::FIXED_NLS_PARAMETERS[:nls_date_format]
188
+ expect(@conn.select("select SYS_CONTEXT('userenv', 'NLS_DATE_FORMAT') as value from dual")).to eq([{ "value" => default }])
189
+ end
190
+ end
191
+
192
+ if defined?(OCI8)
193
+ describe "with TCP keepalive parameters" do
194
+ it "should use database default `tcp_keepalive` value true by default" do
195
+ ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
196
+
197
+ expect(OCI8.properties[:tcp_keepalive]).to be true
198
+ end
199
+
200
+ it "should use modified `tcp_keepalive` value false" do
201
+ ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS.dup.merge(tcp_keepalive: false))
202
+
203
+ expect(OCI8.properties[:tcp_keepalive]).to be false
204
+ end
205
+
206
+ it "should use database default `tcp_keepalive_time` value 600 by default" do
207
+ ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
208
+
209
+ expect(OCI8.properties[:tcp_keepalive_time]).to eq(600)
210
+ end
211
+
212
+ it "should use modified `tcp_keepalive_time` value 3000" do
213
+ ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS.dup.merge(tcp_keepalive_time: 3000))
214
+
215
+ expect(OCI8.properties[:tcp_keepalive_time]).to eq(3000)
216
+ end
217
+ end
218
+ end
219
+
220
+ describe "with non-string parameters" do
221
+ before(:all) do
222
+ params = CONNECTION_PARAMS.dup
223
+ params[:username] = params[:username].to_sym
224
+ params[:password] = params[:password].to_sym
225
+ params[:database] = params[:database].to_sym
226
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
227
+ end
228
+
229
+ it "should create new connection" do
230
+ expect(@conn).to be_active
231
+ end
232
+ end
233
+
234
+ describe "with slash-prefixed database name (service name)" do
235
+ before(:all) do
236
+ params = CONNECTION_PARAMS.dup
237
+ params[:database] = "/#{params[:database]}" unless params[:database].start_with?("/")
238
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
239
+ end
240
+
241
+ it "should create new connection" do
242
+ expect(@conn).to be_active
243
+ end
244
+ end
245
+
246
+ describe "default_timezone" do
247
+ include SchemaSpecHelper
248
+
249
+ before(:all) do
250
+ ActiveRecord::Base.establish_connection(CONNECTION_WITH_TIMEZONE_PARAMS)
251
+ schema_define do
252
+ create_table :posts, force: true do |t|
253
+ t.timestamps null: false
254
+ end
255
+ end
256
+ class ::Post < ActiveRecord::Base
257
+ end
258
+ end
259
+
260
+ after(:all) do
261
+ Object.send(:remove_const, "Post") if defined?(Post)
262
+ ActiveRecord::Base.clear_cache!
263
+ end
264
+
265
+ it "should respect default_timezone = :utc than time_zone setting" do
266
+ # it expects that ActiveRecord.default_timezone = :utc
267
+ ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_WITH_TIMEZONE_PARAMS)
268
+ post = Post.create!
269
+ created_at = post.created_at
270
+ expect(post).to eq(Post.find_by!(created_at: created_at))
271
+ end
272
+ end
273
+
274
+ describe 'with host="connection-string"' do
275
+ let(:username) { CONNECTION_PARAMS[:username] }
276
+ let(:password) { CONNECTION_PARAMS[:password] }
277
+ let(:connection_string) { "(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcp)(HOST=#{DATABASE_HOST})(PORT=#{DATABASE_PORT})))(CONNECT_DATA=(SERVICE_NAME=#{DATABASE_NAME})))" }
278
+ let(:params) { { username: username, password: password, host: "connection-string", database: connection_string } }
279
+
280
+ it "uses the database param as the connection string" do
281
+ if ORACLE_ENHANCED_CONNECTION == :jdbc
282
+ expect(java.sql.DriverManager).to receive(:getConnection).with("jdbc:oracle:thin:@#{connection_string}", anything).and_call_original
283
+ else
284
+ expect(OCI8).to receive(:new).with(username, password, connection_string, nil).and_call_original
285
+ end
286
+ conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
287
+ expect(conn).to be_active
288
+ end
289
+ end
290
+
291
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
292
+
293
+ describe "create JDBC connection" do
294
+ it "should create new connection using :url" do
295
+ params = CONNECTION_PARAMS.dup
296
+ params[:url] = "jdbc:oracle:thin:@#{DATABASE_HOST && "//#{DATABASE_HOST}#{DATABASE_PORT && ":#{DATABASE_PORT}"}/"}#{DATABASE_NAME}"
297
+
298
+ params[:host] = nil
299
+ params[:database] = nil
300
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
301
+ expect(@conn).to be_active
302
+ end
303
+
304
+ it "should create new connection using :url and tnsnames alias" do
305
+ params = CONNECTION_PARAMS.dup
306
+ params[:url] = "jdbc:oracle:thin:@#{DATABASE_NAME}"
307
+ params[:host] = nil
308
+ params[:database] = nil
309
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
310
+ expect(@conn).to be_active
311
+ end
312
+
313
+ it "should create new connection using just tnsnames alias" do
314
+ params = CONNECTION_PARAMS.dup
315
+ params[:host] = nil
316
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
317
+ expect(@conn).to be_active
318
+ end
319
+
320
+ it "should create a new connection using JNDI" do
321
+ begin
322
+ import "oracle.jdbc.driver.OracleDriver"
323
+ import "org.apache.commons.pool.impl.GenericObjectPool"
324
+ import "org.apache.commons.dbcp.PoolingDataSource"
325
+ import "org.apache.commons.dbcp.PoolableConnectionFactory"
326
+ import "org.apache.commons.dbcp.DriverManagerConnectionFactory"
327
+ rescue NameError => e
328
+ return skip e.message
329
+ end
330
+
331
+ class InitialContextMock
332
+ def initialize
333
+ connection_pool = GenericObjectPool.new(nil)
334
+ uri = "jdbc:oracle:thin:@#{DATABASE_HOST && "#{DATABASE_HOST}:"}#{DATABASE_PORT && "#{DATABASE_PORT}:"}#{DATABASE_NAME}"
335
+ connection_factory = DriverManagerConnectionFactory.new(uri, DATABASE_USER, DATABASE_PASSWORD)
336
+ PoolableConnectionFactory.new(connection_factory, connection_pool, nil, nil, false, true)
337
+ @data_source = PoolingDataSource.new(connection_pool)
338
+ @data_source.access_to_underlying_connection_allowed = true
339
+ end
340
+ def lookup(path)
341
+ if path == "java:/comp/env"
342
+ self
343
+ else
344
+ @data_source
345
+ end
346
+ end
347
+ end
348
+
349
+ allow(javax.naming.InitialContext).to receive(:new).and_return(InitialContextMock.new)
350
+
351
+ params = {}
352
+ params[:jndi] = "java:comp/env/jdbc/test"
353
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
354
+ expect(@conn).to be_active
355
+ end
356
+ end
357
+
358
+ it "should fall back to directly instantiating OracleDriver" do
359
+ params = CONNECTION_PARAMS.dup
360
+ params[:url] = "jdbc:oracle:thin:@#{DATABASE_HOST && "//#{DATABASE_HOST}#{DATABASE_PORT && ":#{DATABASE_PORT}"}/"}#{DATABASE_NAME}"
361
+ params[:host] = nil
362
+ params[:database] = nil
363
+ allow(java.sql.DriverManager).to receive(:getConnection).and_raise("no suitable driver found")
364
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(params)
365
+ expect(@conn).to be_active
366
+ end
367
+
368
+ end
369
+
370
+ describe "SQL execution" do
371
+ before(:all) do
372
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
373
+ end
374
+
375
+ it "should execute SQL statement" do
376
+ expect(@conn.exec("SELECT * FROM dual")).not_to be_nil
377
+ end
378
+
379
+ it "should execute SQL select" do
380
+ expect(@conn.select("SELECT * FROM dual")).to eq([{ "dummy" => "X" }])
381
+ end
382
+
383
+ it "should execute SQL select and return also columns" do
384
+ expect(@conn.select("SELECT * FROM dual", nil, true)).to eq([ [{ "dummy" => "X" }], ["dummy"] ])
385
+ end
386
+ end
387
+
388
+ describe "SQL with bind parameters" do
389
+ before(:all) do
390
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
391
+ end
392
+
393
+ it "should execute SQL statement with bind parameter" do
394
+ cursor = @conn.prepare("SELECT * FROM dual WHERE :1 = 1")
395
+ cursor.bind_param(1, 1)
396
+ cursor.exec
397
+ expect(cursor.get_col_names).to eq(["DUMMY"])
398
+ expect(cursor.fetch).to eq(["X"])
399
+ cursor.close
400
+ end
401
+
402
+ it "should execute prepared statement with different bind parameters" do
403
+ cursor = @conn.prepare("SELECT * FROM dual WHERE :1 = 1")
404
+ cursor.bind_param(1, 1)
405
+ cursor.exec
406
+ expect(cursor.fetch).to eq(["X"])
407
+ cursor.bind_param(1, 0)
408
+ cursor.exec
409
+ expect(cursor.fetch).to be_nil
410
+ cursor.close
411
+ end
412
+ end
413
+
414
+ describe "SQL with bind parameters when NLS_NUMERIC_CHARACTERS is set to ', '" do
415
+ before(:all) do
416
+ ENV["NLS_NUMERIC_CHARACTERS"] = ", "
417
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
418
+ @conn_base = ActiveRecord::Base.connection
419
+ @conn = @conn_base.send(:_connection)
420
+ @conn.exec "CREATE TABLE test_employees (age NUMBER(10,2))"
421
+ end
422
+
423
+ after(:all) do
424
+ ENV["NLS_NUMERIC_CHARACTERS"] = nil
425
+ @conn.exec "DROP TABLE test_employees" rescue nil
426
+ ActiveRecord::Base.clear_cache!
427
+ end
428
+
429
+ it "should execute prepared statement with decimal bind parameter" do
430
+ cursor = @conn.prepare("INSERT INTO test_employees VALUES(:1)")
431
+ type_metadata = ActiveRecord::ConnectionAdapters::SqlTypeMetadata.new(sql_type: "NUMBER", type: :decimal, limit: 10, precision: nil, scale: 2)
432
+ cast_type = @conn_base.lookup_cast_type("NUMBER(10,2)")
433
+ column = ActiveRecord::ConnectionAdapters::OracleEnhanced::Column.new("age", cast_type, nil, type_metadata, false, comment: nil)
434
+ expect(column.type).to eq(:decimal)
435
+ # Here 1.5 expects that this value has been type casted already
436
+ # it should use bind_params in the long term.
437
+ cursor.bind_param(1, 1.5)
438
+ cursor.exec
439
+ cursor.close
440
+ cursor = @conn.prepare("SELECT age FROM test_employees")
441
+ cursor.exec
442
+ expect(cursor.fetch).to eq([1.5])
443
+ cursor.close
444
+ end
445
+ end
446
+
447
+ describe "auto reconnection" do
448
+ include SchemaSpecHelper
449
+
450
+ before(:all) do
451
+ ActiveRecord::Base.establish_connection(CONNECTION_PARAMS)
452
+ @conn = ActiveRecord::Base.connection.send(:_connection)
453
+ @sys_conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(SYS_CONNECTION_PARAMS)
454
+ schema_define do
455
+ create_table :posts, force: true
456
+ end
457
+ class ::Post < ActiveRecord::Base
458
+ end
459
+ end
460
+
461
+ after(:all) do
462
+ Object.send(:remove_const, "Post")
463
+ ActiveRecord::Base.clear_cache!
464
+ end
465
+
466
+ before(:each) do
467
+ ActiveRecord::Base.connection.reconnect! unless @conn.active?
468
+ end
469
+
470
+ def kill_current_session
471
+ audsid = @conn.select("SELECT userenv('sessionid') audsid FROM dual").first["audsid"]
472
+ sid_serial = @sys_conn.select("SELECT s.sid||','||s.serial# sid_serial
473
+ FROM v$session s
474
+ WHERE audsid = '#{audsid}'").first["sid_serial"]
475
+ @sys_conn.exec "ALTER SYSTEM KILL SESSION '#{sid_serial}' IMMEDIATE"
476
+ end
477
+
478
+ it "should reconnect and execute SQL statement if connection is lost and auto retry is enabled" do
479
+ # @conn.auto_retry = true
480
+ ActiveRecord::Base.connection.auto_retry = true
481
+ kill_current_session
482
+ expect(@conn.exec("SELECT * FROM dual")).not_to be_nil
483
+ end
484
+
485
+ it "should reconnect and execute SQL statement if connection is lost and allow_retry is passed" do
486
+ kill_current_session
487
+ expect(@conn.exec("SELECT * FROM dual", allow_retry: true)).not_to be_nil
488
+ end
489
+
490
+ it "should not reconnect and execute SQL statement if connection is lost and auto retry is disabled" do
491
+ # @conn.auto_retry = false
492
+ ActiveRecord::Base.connection.auto_retry = false
493
+ kill_current_session
494
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
495
+ expect { @conn.exec("SELECT * FROM dual") }.to raise_error(Java::JavaSql::SQLRecoverableException)
496
+ else
497
+ expect { @conn.exec("SELECT * FROM dual") }.to raise_error(OCIError)
498
+ end
499
+ end
500
+
501
+ it "should reconnect and execute SQL select if connection is lost and auto retry is enabled" do
502
+ # @conn.auto_retry = true
503
+ ActiveRecord::Base.connection.auto_retry = true
504
+ kill_current_session
505
+ expect(@conn.select("SELECT * FROM dual")).to eq([{ "dummy" => "X" }])
506
+ end
507
+
508
+ it "should not reconnect and execute SQL select if connection is lost and auto retry is disabled" do
509
+ # @conn.auto_retry = false
510
+ ActiveRecord::Base.connection.auto_retry = false
511
+ kill_current_session
512
+ if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
513
+ expect { @conn.select("SELECT * FROM dual") }.to raise_error(Java::JavaSql::SQLRecoverableException)
514
+ else
515
+ expect { @conn.select("SELECT * FROM dual") }.to raise_error(OCIError)
516
+ end
517
+ end
518
+
519
+ it "should reconnect and execute query if connection is lost and auto retry is enabled" do
520
+ Post.create!
521
+ ActiveRecord::Base.connection.auto_retry = true
522
+ kill_current_session
523
+ expect(Post.take).not_to be_nil
524
+ end
525
+
526
+ it "should not reconnect and execute query if connection is lost and auto retry is disabled" do
527
+ Post.create!
528
+ ActiveRecord::Base.connection.auto_retry = false
529
+ kill_current_session
530
+ expect { Post.take }.to raise_error(ActiveRecord::StatementInvalid)
531
+ end
532
+ end
533
+
534
+ describe "describe table" do
535
+ before(:all) do
536
+ @conn = ActiveRecord::ConnectionAdapters::OracleEnhanced::Connection.create(CONNECTION_PARAMS)
537
+ @owner = CONNECTION_PARAMS[:username].upcase
538
+ end
539
+
540
+ it "should describe existing table" do
541
+ @conn.exec "CREATE TABLE test_employees (first_name VARCHAR2(20))" rescue nil
542
+ expect(@conn.describe("test_employees")).to eq([@owner, "TEST_EMPLOYEES"])
543
+ @conn.exec "DROP TABLE test_employees" rescue nil
544
+ end
545
+
546
+ it "should not describe non-existing table" do
547
+ expect { @conn.describe("test_xxx") }.to raise_error(ActiveRecord::ConnectionAdapters::OracleEnhanced::ConnectionException)
548
+ end
549
+
550
+ it "should describe table in other schema" do
551
+ expect(@conn.describe("sys.dual")).to eq(["SYS", "DUAL"])
552
+ end
553
+
554
+ it "should describe table in other schema if the schema and table are in different cases" do
555
+ expect(@conn.describe("SYS.dual")).to eq(["SYS", "DUAL"])
556
+ end
557
+
558
+ it "should describe existing view" do
559
+ @conn.exec "CREATE TABLE test_employees (first_name VARCHAR2(20))" rescue nil
560
+ @conn.exec "CREATE VIEW test_employees_v AS SELECT * FROM test_employees" rescue nil
561
+ expect(@conn.describe("test_employees_v")).to eq([@owner, "TEST_EMPLOYEES_V"])
562
+ @conn.exec "DROP VIEW test_employees_v" rescue nil
563
+ @conn.exec "DROP TABLE test_employees" rescue nil
564
+ end
565
+
566
+ it "should describe view in other schema" do
567
+ expect(@conn.describe("sys.v_$version")).to eq(["SYS", "V_$VERSION"])
568
+ end
569
+
570
+ it "should describe existing private synonym" do
571
+ @conn.exec "CREATE SYNONYM test_dual FOR sys.dual" rescue nil
572
+ expect(@conn.describe("test_dual")).to eq(["SYS", "DUAL"])
573
+ @conn.exec "DROP SYNONYM test_dual" rescue nil
574
+ end
575
+
576
+ it "should describe existing public synonym" do
577
+ expect(@conn.describe("all_tables")).to eq(["SYS", "ALL_TABLES"])
578
+ end
579
+
580
+ if defined?(OCI8)
581
+ context "OCI8 adapter" do
582
+ it "should not fallback to SELECT-based logic when querying non-existent table information" do
583
+ expect(@conn).not_to receive(:select_one)
584
+ @conn.describe("non_existent") rescue ActiveRecord::ConnectionAdapters::OracleEnhanced::ConnectionException
585
+ end
586
+ end
587
+ end
588
+ end
589
+ end