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,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord # :nodoc:
4
+ module ConnectionAdapters # :nodoc:
5
+ module OracleEnhanced # :nodoc:
6
+ module Lob # :nodoc:
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ class_attribute :custom_create_method, :custom_update_method, :custom_delete_method
11
+
12
+ # After setting large objects to empty, select the OCI8::LOB
13
+ # and write back the data.
14
+ before_create :record_lobs_for_create
15
+ after_create :enhanced_write_lobs
16
+ before_update :record_changed_lobs
17
+ after_update :enhanced_write_lobs
18
+ end
19
+
20
+ module ClassMethods
21
+ def lob_columns
22
+ columns.select do |column|
23
+ column.sql_type_metadata.sql_type.end_with?("LOB")
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+ def enhanced_write_lobs
30
+ # Skip if using prepared statements - LOB data is already written via temp LOB binding
31
+ return if self.class.connection.prepared_statements
32
+
33
+ if self.class.connection.is_a?(ConnectionAdapters::OracleEnhancedAdapter) &&
34
+ !(self.class.custom_create_method || self.class.custom_update_method)
35
+ self.class.connection.write_lobs(self.class.table_name, self.class, attributes, @changed_lob_columns)
36
+ end
37
+ end
38
+
39
+ def record_lobs_for_create
40
+ @changed_lob_columns = self.class.lob_columns.select do |col|
41
+ !attributes[col.name].nil? && !self.class.readonly_attributes.to_a.include?(col.name)
42
+ end
43
+ end
44
+
45
+ def record_changed_lobs
46
+ @changed_lob_columns = self.class.lob_columns.select do |col|
47
+ self.will_save_change_to_attribute?(col.name) && !self.class.readonly_attributes.to_a.include?(col.name)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ ActiveSupport.on_load(:active_record) do
56
+ ActiveRecord::Base.send(:include, ActiveRecord::ConnectionAdapters::OracleEnhanced::Lob)
57
+ end
@@ -0,0 +1,465 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ begin
6
+ require "oci8"
7
+ rescue LoadError => e
8
+ # OCI8 driver is unavailable or failed to load a required library.
9
+ raise LoadError, "ERROR: '#{e.message}'. "\
10
+ "ActiveRecord oracle_enhanced adapter could not load ruby-oci8 library. "\
11
+ "You may need install ruby-oci8 gem."
12
+ end
13
+
14
+ # check ruby-oci8 version
15
+ required_oci8_version = [2, 2, 4]
16
+ oci8_version_ints = OCI8::VERSION.scan(/\d+/).map { |s| s.to_i }
17
+ if (oci8_version_ints <=> required_oci8_version) < 0
18
+ $stderr.puts <<~EOS
19
+ "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version #{required_oci8_version.join('.')} or later."
20
+ EOS
21
+
22
+ exit!
23
+ end
24
+
25
+ module ActiveRecord
26
+ module ConnectionAdapters
27
+ # OCI database interface for MRI
28
+ module OracleEnhanced
29
+ class OCIConnection < OracleEnhanced::Connection # :nodoc:
30
+ def initialize(config)
31
+ @raw_connection = OCI8EnhancedAutoRecover.new(config, OracleEnhancedOCIFactory)
32
+ # default schema owner
33
+ @owner = config[:schema]
34
+ @owner ||= config[:username]
35
+ @owner = @owner.to_s.upcase
36
+ end
37
+
38
+ def raw_oci_connection
39
+ if @raw_connection.is_a? OCI8
40
+ @raw_connection
41
+ # ActiveRecord Oracle enhanced adapter puts OCI8EnhancedAutoRecover wrapper around OCI8
42
+ # in this case we need to pass original OCI8 connection
43
+ else
44
+ @raw_connection.instance_variable_get(:@raw_connection)
45
+ end
46
+ end
47
+
48
+ def auto_retry
49
+ @raw_connection.auto_retry if @raw_connection
50
+ end
51
+
52
+ def auto_retry=(value)
53
+ @raw_connection.auto_retry = value if @raw_connection
54
+ end
55
+
56
+ def logoff
57
+ @raw_connection.logoff
58
+ @raw_connection.active = false
59
+ end
60
+
61
+ def commit
62
+ @raw_connection.commit
63
+ end
64
+
65
+ def rollback
66
+ @raw_connection.rollback
67
+ end
68
+
69
+ def autocommit?
70
+ @raw_connection.autocommit?
71
+ end
72
+
73
+ def autocommit=(value)
74
+ @raw_connection.autocommit = value
75
+ end
76
+
77
+ # Checks connection, returns true if active. Note that ping actively
78
+ # checks the connection, while #active? simply returns the last
79
+ # known state.
80
+ def ping
81
+ @raw_connection.ping
82
+ rescue OCIException => e
83
+ raise OracleEnhanced::ConnectionException, e.message
84
+ end
85
+
86
+ def active?
87
+ @raw_connection.active?
88
+ end
89
+
90
+ def reset
91
+ @raw_connection.reset
92
+ end
93
+
94
+ def reset!
95
+ @raw_connection.reset!
96
+ rescue OCIException => e
97
+ raise OracleEnhanced::ConnectionException, e.message
98
+ end
99
+
100
+ def exec(sql, *bindvars, allow_retry: false, &block)
101
+ with_retry(allow_retry: allow_retry) { @raw_connection.exec(sql, *bindvars, &block) }
102
+ end
103
+
104
+ def with_retry(allow_retry: false, &block)
105
+ @raw_connection.with_retry(allow_retry: allow_retry, &block)
106
+ end
107
+
108
+ def prepare(sql)
109
+ Cursor.new(self, @raw_connection.parse(sql))
110
+ end
111
+
112
+ class Cursor
113
+ def initialize(connection, raw_cursor)
114
+ @raw_connection = connection
115
+ @raw_cursor = raw_cursor
116
+ end
117
+
118
+ def bind_params(*bind_vars)
119
+ index = 1
120
+ bind_vars.flatten.each do |var|
121
+ if Hash === var
122
+ var.each { |key, val| bind_param key, val }
123
+ else
124
+ bind_param index, var
125
+ index += 1
126
+ end
127
+ end
128
+ end
129
+
130
+ def bind_param(position, value)
131
+ case value
132
+ when Type::OracleEnhanced::Raw
133
+ @raw_cursor.bind_param(position, OracleEnhanced::Quoting.encode_raw(value))
134
+ when ActiveModel::Type::Decimal
135
+ @raw_cursor.bind_param(position, BigDecimal(value.to_s))
136
+ when Type::OracleEnhanced::CharacterString::Data
137
+ @raw_cursor.bind_param(position, value.to_character_str)
138
+ when NilClass
139
+ @raw_cursor.bind_param(position, nil, String)
140
+ else
141
+ @raw_cursor.bind_param(position, value)
142
+ end
143
+ end
144
+
145
+ def bind_returning_param(position, bind_type)
146
+ @raw_cursor.bind_param(position, nil, bind_type)
147
+ end
148
+
149
+ def exec
150
+ @raw_cursor.exec
151
+ end
152
+
153
+ def exec_update
154
+ @raw_cursor.exec
155
+ end
156
+
157
+ def get_col_names
158
+ @raw_cursor.get_col_names
159
+ end
160
+
161
+ def row_count
162
+ @raw_cursor.row_count
163
+ end
164
+
165
+ def select_statement?
166
+ @raw_cursor.type == :select_stmt
167
+ end
168
+
169
+ def fetch(options = {})
170
+ if row = @raw_cursor.fetch
171
+ get_lob_value = options[:get_lob_value]
172
+ col_index = 0
173
+ row.map do |col|
174
+ col_value = @raw_connection.typecast_result_value(col, get_lob_value)
175
+ col_metadata = @raw_cursor.column_metadata.fetch(col_index)
176
+ if !col_metadata.nil?
177
+ key = col_metadata.data_type
178
+ case key.to_s.downcase
179
+ when "char"
180
+ col_value = col.to_s.rstrip
181
+ end
182
+ end
183
+ col_index = col_index + 1
184
+ col_value
185
+ end
186
+ end
187
+ end
188
+
189
+ def get_returning_param(position, type)
190
+ @raw_cursor[position]
191
+ end
192
+
193
+ def close
194
+ @raw_cursor.close
195
+ end
196
+ end
197
+
198
+ def select(sql, name = nil, return_column_names = false)
199
+ cursor = @raw_connection.exec(sql)
200
+ cols = []
201
+ # Ignore raw_rnum_ which is used to simulate LIMIT and OFFSET
202
+ cursor.get_col_names.each do |col_name|
203
+ col_name = _oracle_downcase(col_name)
204
+ cols << col_name unless col_name == "raw_rnum_"
205
+ end
206
+ # Reuse the same hash for all rows
207
+ column_hash = {}
208
+ cols.each { |c| column_hash[c] = nil }
209
+ rows = []
210
+ get_lob_value = !(name == "Writable Large Object")
211
+
212
+ while row = cursor.fetch
213
+ hash = column_hash.dup
214
+
215
+ cols.each_with_index do |col, i|
216
+ col_value = typecast_result_value(row[i], get_lob_value)
217
+ col_metadata = cursor.column_metadata.fetch(i)
218
+ if !col_metadata.nil?
219
+ key = col_metadata.data_type
220
+ case key.to_s.downcase
221
+ when "char"
222
+ col_value = col_value.to_s.rstrip
223
+ end
224
+ end
225
+ hash[col] = col_value
226
+ end
227
+
228
+ rows << hash
229
+ end
230
+
231
+ return_column_names ? [rows, cols] : rows
232
+ ensure
233
+ cursor.close if cursor
234
+ end
235
+
236
+ def write_lob(lob, value, is_binary = false)
237
+ lob.write value
238
+ end
239
+
240
+ def describe(name)
241
+ super
242
+ end
243
+
244
+ # Return OCIError error code
245
+ def error_code(exception)
246
+ case exception
247
+ when OCIError
248
+ exception.code
249
+ else
250
+ nil
251
+ end
252
+ end
253
+
254
+ def typecast_result_value(value, get_lob_value)
255
+ case value
256
+ when Integer
257
+ value
258
+ when String
259
+ value
260
+ when Float, BigDecimal
261
+ # return Integer if value is integer (to avoid issues with _before_type_cast values for id attributes)
262
+ value == (v_to_i = value.to_i) ? v_to_i : value
263
+ when OCI8::LOB
264
+ if get_lob_value
265
+ data = value.read || "" # if value.read returns nil, then we have an empty_clob() i.e. an empty string
266
+ # In Ruby 1.9.1 always change encoding to ASCII-8BIT for binaries
267
+ data.force_encoding("ASCII-8BIT") if data.respond_to?(:force_encoding) && value.is_a?(OCI8::BLOB)
268
+ data
269
+ else
270
+ value
271
+ end
272
+ when Time, DateTime
273
+ create_time_with_default_timezone(value)
274
+ else
275
+ value
276
+ end
277
+ end
278
+
279
+ def database_version
280
+ @database_version ||= (version = raw_connection.oracle_server_version) && [version.major, version.minor]
281
+ end
282
+
283
+ private
284
+ def date_without_time?(value)
285
+ case value
286
+ when OraDate
287
+ value.hour == 0 && value.minute == 0 && value.second == 0
288
+ else
289
+ value.hour == 0 && value.min == 0 && value.sec == 0
290
+ end
291
+ end
292
+
293
+ def create_time_with_default_timezone(value)
294
+ year, month, day, hour, min, sec, usec = case value
295
+ when Time
296
+ [value.year, value.month, value.day, value.hour, value.min, value.sec, value.usec]
297
+ when OraDate
298
+ [value.year, value.month, value.day, value.hour, value.minute, value.second, 0]
299
+ else
300
+ [value.year, value.month, value.day, value.hour, value.min, value.sec, 0]
301
+ end
302
+ # code from Time.time_with_datetime_fallback
303
+ begin
304
+ Time.send(ActiveRecord.default_timezone, year, month, day, hour, min, sec, usec)
305
+ rescue
306
+ offset = ActiveRecord.default_timezone.to_sym == :local ? ::DateTime.local_offset : 0
307
+ ::DateTime.civil(year, month, day, hour, min, sec, offset)
308
+ end
309
+ end
310
+ end
311
+
312
+ # The OracleEnhancedOCIFactory factors out the code necessary to connect and
313
+ # configure an Oracle/OCI connection.
314
+ class OracleEnhancedOCIFactory # :nodoc:
315
+ DEFAULT_TCP_KEEPALIVE_TIME = 600
316
+
317
+ def self.new_connection(config)
318
+ # to_s needed if username, password or database is specified as number in database.yml file
319
+ username = config[:username] && config[:username].to_s
320
+ password = config[:password] && config[:password].to_s
321
+ database = config[:database] && config[:database].to_s
322
+ schema = config[:schema] && config[:schema].to_s
323
+ host, port = config[:host], config[:port]
324
+ privilege = config[:privilege] && config[:privilege].to_sym
325
+ async = config[:allow_concurrency]
326
+ prefetch_rows = config[:prefetch_rows] || 100
327
+ cursor_sharing = config[:cursor_sharing] || "force"
328
+ # get session time_zone from configuration or from TZ environment variable
329
+ time_zone = config[:time_zone] || ENV["TZ"]
330
+
331
+ # using a connection string via DATABASE_URL
332
+ connection_string = if host == "connection-string"
333
+ database
334
+ # connection using host, port and database name
335
+ elsif host || port
336
+ host ||= "localhost"
337
+ host = "[#{host}]" if /^[^\[].*:/.match?(host) # IPv6
338
+ port ||= 1521
339
+ database = "/#{database}" unless database.start_with?("/")
340
+ "//#{host}:#{port}#{database}"
341
+ # if no host is specified then assume that
342
+ # database parameter is TNS alias or TNS connection string
343
+ else
344
+ database
345
+ end
346
+
347
+ OCI8.properties[:tcp_keepalive] = config[:tcp_keepalive] == false ? false : true
348
+ begin
349
+ OCI8.properties[:tcp_keepalive_time] = config[:tcp_keepalive_time] || DEFAULT_TCP_KEEPALIVE_TIME
350
+ rescue NotImplementedError
351
+ end
352
+
353
+ conn = OCI8.new username, password, connection_string, privilege
354
+ conn.autocommit = true
355
+ conn.non_blocking = true if async
356
+ conn.prefetch_rows = prefetch_rows
357
+ conn.exec "alter session set cursor_sharing = #{cursor_sharing}" rescue nil if cursor_sharing
358
+ if ActiveRecord.default_timezone == :local
359
+ conn.exec "alter session set time_zone = '#{time_zone}'" unless time_zone.blank?
360
+ elsif ActiveRecord.default_timezone == :utc
361
+ conn.exec "alter session set time_zone = '+00:00'"
362
+ end
363
+ conn.exec "alter session set current_schema = #{schema}" unless schema.blank?
364
+
365
+ # Initialize NLS parameters
366
+ OracleEnhancedAdapter::DEFAULT_NLS_PARAMETERS.each do |key, default_value|
367
+ value = config[key] || ENV[key.to_s.upcase] || default_value
368
+ if value
369
+ conn.exec "alter session set #{key} = '#{value}'"
370
+ end
371
+ end
372
+
373
+ OracleEnhancedAdapter::FIXED_NLS_PARAMETERS.each do |key, value|
374
+ conn.exec "alter session set #{key} = '#{value}'"
375
+ end
376
+ conn
377
+ end
378
+ end
379
+ end
380
+ end
381
+ end
382
+
383
+ # The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and
384
+ # reset functionality. If a call to #exec fails, and autocommit is turned on
385
+ # (ie., we're not in the middle of a longer transaction), it will
386
+ # automatically reconnect and try again. If autocommit is turned off,
387
+ # this would be dangerous (as the earlier part of the implied transaction
388
+ # may have failed silently if the connection died) -- so instead the
389
+ # connection is marked as dead, to be reconnected on it's next use.
390
+ # :stopdoc:
391
+ class OCI8EnhancedAutoRecover < DelegateClass(OCI8) # :nodoc:
392
+ attr_accessor :active # :nodoc:
393
+ alias :active? :active # :nodoc:
394
+
395
+ cattr_accessor :auto_retry
396
+ class << self
397
+ alias :auto_retry? :auto_retry # :nodoc:
398
+ end
399
+ @@auto_retry = false
400
+
401
+ def initialize(config, factory) # :nodoc:
402
+ @active = true
403
+ @config = config
404
+ @factory = factory
405
+ @raw_connection = @factory.new_connection @config
406
+ super @raw_connection
407
+ end
408
+
409
+ # Checks connection, returns true if active. Note that ping actively
410
+ # checks the connection, while #active? simply returns the last
411
+ # known state.
412
+ def ping # :nodoc:
413
+ @raw_connection.exec("select 1 from dual") { |r| nil }
414
+ @active = true
415
+ rescue
416
+ @active = false
417
+ raise
418
+ end
419
+
420
+ def reset
421
+ # tentative
422
+ reset!
423
+ end
424
+
425
+ # Resets connection, by logging off and creating a new connection.
426
+ def reset! # :nodoc:
427
+ logoff rescue nil
428
+ begin
429
+ @raw_connection = @factory.new_connection @config
430
+ __setobj__ @raw_connection
431
+ @active = true
432
+ rescue
433
+ @active = false
434
+ raise
435
+ end
436
+ end
437
+
438
+ # ORA-00028: your session has been killed
439
+ # ORA-01012: not logged on
440
+ # ORA-03113: end-of-file on communication channel
441
+ # ORA-03114: not connected to ORACLE
442
+ # ORA-03135: connection lost contact
443
+ LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114, 3135 ] # :nodoc:
444
+
445
+ # Adds auto-recovery functionality.
446
+ def with_retry(allow_retry: false) # :nodoc:
447
+ should_retry = (allow_retry || self.class.auto_retry?) && autocommit?
448
+
449
+ begin
450
+ yield
451
+ rescue OCIException => e
452
+ raise unless e.is_a?(OCIError) && LOST_CONNECTION_ERROR_CODES.include?(e.code)
453
+ @active = false
454
+ raise unless should_retry
455
+ should_retry = false
456
+ reset! rescue nil
457
+ retry
458
+ end
459
+ end
460
+
461
+ def exec(sql, *bindvars, &block) # :nodoc:
462
+ with_retry { @raw_connection.exec(sql, *bindvars, &block) }
463
+ end
464
+ end
465
+ # :startdoc:
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module OracleEnhanced
6
+ module OCIQuoting
7
+ def type_cast(value)
8
+ case value
9
+ when ActiveModel::Type::Binary::Data
10
+ lob_value = value == "" ? " " : value
11
+ bind_type = OCI8::BLOB
12
+ ora_value = bind_type.new(_connection.raw_oci_connection, lob_value)
13
+ ora_value.size = 0 if value == ""
14
+ ora_value
15
+ when Type::OracleEnhanced::Text::Data
16
+ lob_value = value.to_s == "" ? " " : value.to_s
17
+ bind_type = OCI8::CLOB
18
+ ora_value = bind_type.new(_connection.raw_oci_connection, lob_value)
19
+ ora_value.size = 0 if value.to_s == ""
20
+ ora_value
21
+ when Type::OracleEnhanced::NationalCharacterText::Data
22
+ lob_value = value.to_s == "" ? " " : value.to_s
23
+ bind_type = OCI8::NCLOB
24
+ ora_value = bind_type.new(_connection.raw_oci_connection, lob_value)
25
+ ora_value.size = 0 if value.to_s == ""
26
+ ora_value
27
+ else
28
+ super
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ module ActiveRecord
37
+ module ConnectionAdapters
38
+ module OracleEnhanced
39
+ module Quoting
40
+ prepend OCIQuoting
41
+ end
42
+ end
43
+ end
44
+ end