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