oracle_enhanced 1.2.5

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 (38) hide show
  1. data/.gitignore +10 -0
  2. data/History.txt +182 -0
  3. data/License.txt +20 -0
  4. data/Manifest.txt +32 -0
  5. data/README.rdoc +77 -0
  6. data/Rakefile +49 -0
  7. data/VERSION +1 -0
  8. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +5 -0
  9. data/lib/active_record/connection_adapters/oracle_enhanced.rake +51 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +1661 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +121 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +64 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +21 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +39 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +393 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +389 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +163 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced_reserved_words.rb +126 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +168 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +213 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +224 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +11 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -0
  24. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +477 -0
  25. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_structure_dumper_spec.rb +267 -0
  26. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +206 -0
  27. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +40 -0
  28. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +107 -0
  29. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +984 -0
  30. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +67 -0
  31. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +93 -0
  32. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +25 -0
  33. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +370 -0
  34. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +203 -0
  35. data/spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb +784 -0
  36. data/spec/spec.opts +6 -0
  37. data/spec/spec_helper.rb +114 -0
  38. metadata +140 -0
@@ -0,0 +1,389 @@
1
+ require 'delegate'
2
+
3
+ begin
4
+ require "oci8"
5
+ rescue LoadError
6
+ # OCI8 driver is unavailable.
7
+ raise LoadError, "ERROR: ActiveRecord oracle_enhanced adapter could not load ruby-oci8 library. Please install ruby-oci8 gem."
8
+ end
9
+
10
+ # check ruby-oci8 version
11
+ required_oci8_version = [2, 0, 3]
12
+ oci8_version_ints = OCI8::VERSION.scan(/\d+/).map{|s| s.to_i}
13
+ if (oci8_version_ints <=> required_oci8_version) < 0
14
+ raise LoadError, "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version #{required_oci8_version.join('.')} or later."
15
+ end
16
+
17
+ module ActiveRecord
18
+ module ConnectionAdapters
19
+
20
+ # OCI database interface for MRI
21
+ class OracleEnhancedOCIConnection < OracleEnhancedConnection #:nodoc:
22
+
23
+ def initialize(config)
24
+ @raw_connection = OCI8EnhancedAutoRecover.new(config, OracleEnhancedOCIFactory)
25
+ # default schema owner
26
+ @owner = config[:username].to_s.upcase
27
+ end
28
+
29
+ def auto_retry
30
+ @raw_connection.auto_retry if @raw_connection
31
+ end
32
+
33
+ def auto_retry=(value)
34
+ @raw_connection.auto_retry = value if @raw_connection
35
+ end
36
+
37
+ def logoff
38
+ @raw_connection.logoff
39
+ @raw_connection.active = false
40
+ end
41
+
42
+ def commit
43
+ @raw_connection.commit
44
+ end
45
+
46
+ def rollback
47
+ @raw_connection.rollback
48
+ end
49
+
50
+ def autocommit?
51
+ @raw_connection.autocommit?
52
+ end
53
+
54
+ def autocommit=(value)
55
+ @raw_connection.autocommit = value
56
+ end
57
+
58
+ # Checks connection, returns true if active. Note that ping actively
59
+ # checks the connection, while #active? simply returns the last
60
+ # known state.
61
+ def ping
62
+ @raw_connection.ping
63
+ rescue OCIException => e
64
+ raise OracleEnhancedConnectionException, e.message
65
+ end
66
+
67
+ def active?
68
+ @raw_connection.active?
69
+ end
70
+
71
+ def reset!
72
+ @raw_connection.reset!
73
+ rescue OCIException => e
74
+ raise OracleEnhancedConnectionException, e.message
75
+ end
76
+
77
+ def exec(sql, *bindvars, &block)
78
+ @raw_connection.exec(sql, *bindvars, &block)
79
+ end
80
+
81
+ def returning_clause(quoted_pk)
82
+ " RETURNING #{quoted_pk} INTO :insert_id"
83
+ end
84
+
85
+ # execute sql with RETURNING ... INTO :insert_id
86
+ # and return :insert_id value
87
+ def exec_with_returning(sql)
88
+ cursor = @raw_connection.parse(sql)
89
+ cursor.bind_param(':insert_id', nil, Integer)
90
+ cursor.exec
91
+ cursor[':insert_id']
92
+ ensure
93
+ cursor.close rescue nil
94
+ end
95
+
96
+ def select(sql, name = nil, return_column_names = false)
97
+ cursor = @raw_connection.exec(sql)
98
+ cols = []
99
+ # Ignore raw_rnum_ which is used to simulate LIMIT and OFFSET
100
+ cursor.get_col_names.each do |col_name|
101
+ col_name = oracle_downcase(col_name)
102
+ cols << col_name unless col_name == 'raw_rnum_'
103
+ end
104
+ # Reuse the same hash for all rows
105
+ column_hash = {}
106
+ cols.each {|c| column_hash[c] = nil}
107
+ rows = []
108
+ get_lob_value = !(name == 'Writable Large Object')
109
+
110
+ while row = cursor.fetch
111
+ hash = column_hash.dup
112
+
113
+ cols.each_with_index do |col, i|
114
+ hash[col] = typecast_result_value(row[i], get_lob_value)
115
+ end
116
+
117
+ rows << hash
118
+ end
119
+
120
+ return_column_names ? [rows, cols] : rows
121
+ ensure
122
+ cursor.close if cursor
123
+ end
124
+
125
+ def write_lob(lob, value, is_binary = false)
126
+ lob.write value
127
+ end
128
+
129
+ def describe(name)
130
+ # fall back to SELECT based describe if using database link
131
+ return super if name.to_s.include?('@')
132
+ quoted_name = OracleEnhancedAdapter.valid_table_name?(name) ? name : "\"#{name}\""
133
+ @raw_connection.describe(quoted_name)
134
+ rescue OCIException => e
135
+ # fall back to SELECT which can handle synonyms to database links
136
+ super
137
+ end
138
+
139
+ # Return OCIError error code
140
+ def error_code(exception)
141
+ exception.code
142
+ end
143
+
144
+ private
145
+
146
+ def typecast_result_value(value, get_lob_value)
147
+ case value
148
+ when Fixnum, Bignum
149
+ value
150
+ when String
151
+ value
152
+ when Float, BigDecimal
153
+ # return Fixnum or Bignum if value is integer (to avoid issues with _before_type_cast values for id attributes)
154
+ value == (v_to_i = value.to_i) ? v_to_i : value
155
+ when OraNumber
156
+ # change OraNumber value (returned in early versions of ruby-oci8 2.0.x) to BigDecimal
157
+ value == (v_to_i = value.to_i) ? v_to_i : BigDecimal.new(value.to_s)
158
+ when OCI8::LOB
159
+ if get_lob_value
160
+ data = value.read
161
+ # In Ruby 1.9.1 always change encoding to ASCII-8BIT for binaries
162
+ data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding) && value.is_a?(OCI8::BLOB)
163
+ data
164
+ else
165
+ value
166
+ end
167
+ # ruby-oci8 1.0 returns OraDate
168
+ # ruby-oci8 2.0 returns Time or DateTime
169
+ when OraDate, Time, DateTime
170
+ if OracleEnhancedAdapter.emulate_dates && date_without_time?(value)
171
+ value.to_date
172
+ else
173
+ create_time_with_default_timezone(value)
174
+ end
175
+ else
176
+ value
177
+ end
178
+ end
179
+
180
+ def date_without_time?(value)
181
+ case value
182
+ when OraDate
183
+ value.hour == 0 && value.minute == 0 && value.second == 0
184
+ else
185
+ value.hour == 0 && value.min == 0 && value.sec == 0
186
+ end
187
+ end
188
+
189
+ def create_time_with_default_timezone(value)
190
+ year, month, day, hour, min, sec, usec = case value
191
+ when Time
192
+ [value.year, value.month, value.day, value.hour, value.min, value.sec, value.usec]
193
+ when OraDate
194
+ [value.year, value.month, value.day, value.hour, value.minute, value.second, 0]
195
+ else
196
+ [value.year, value.month, value.day, value.hour, value.min, value.sec, 0]
197
+ end
198
+ # code from Time.time_with_datetime_fallback
199
+ begin
200
+ Time.send(Base.default_timezone, year, month, day, hour, min, sec, usec)
201
+ rescue
202
+ offset = Base.default_timezone.to_sym == :local ? ::DateTime.local_offset : 0
203
+ ::DateTime.civil(year, month, day, hour, min, sec, offset)
204
+ end
205
+ end
206
+
207
+ end
208
+
209
+ # The OracleEnhancedOCIFactory factors out the code necessary to connect and
210
+ # configure an Oracle/OCI connection.
211
+ class OracleEnhancedOCIFactory #:nodoc:
212
+ def self.new_connection(config)
213
+ username, password, database = config[:username].to_s, config[:password].to_s, config[:database].to_s
214
+ privilege = config[:privilege] && config[:privilege].to_sym
215
+ async = config[:allow_concurrency]
216
+ prefetch_rows = config[:prefetch_rows] || 100
217
+ cursor_sharing = config[:cursor_sharing] || 'force'
218
+ # by default VARCHAR2 column size will be interpreted as max number of characters (and not bytes)
219
+ nls_length_semantics = config[:nls_length_semantics] || 'CHAR'
220
+ # get session time_zone from configuration or from TZ environment variable
221
+ time_zone = config[:time_zone] || ENV['TZ']
222
+
223
+ conn = OCI8.new username, password, database, privilege
224
+ conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'}
225
+ conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS:FF6'} rescue nil
226
+ conn.autocommit = true
227
+ conn.non_blocking = true if async
228
+ conn.prefetch_rows = prefetch_rows
229
+ conn.exec "alter session set cursor_sharing = #{cursor_sharing}" rescue nil
230
+ conn.exec "alter session set nls_length_semantics = '#{nls_length_semantics}'"
231
+ conn.exec "alter session set time_zone = '#{time_zone}'" unless time_zone.blank?
232
+ conn
233
+ end
234
+ end
235
+
236
+
237
+ end
238
+ end
239
+
240
+
241
+
242
+ class OCI8 #:nodoc:
243
+
244
+ class Cursor #:nodoc:
245
+ if method_defined? :define_a_column
246
+ # This OCI8 patch is required with the ruby-oci8 1.0.x or lower.
247
+ # Set OCI8::BindType::Mapping[] to change the column type
248
+ # when using ruby-oci8 2.0.
249
+
250
+ alias :enhanced_define_a_column_pre_ar :define_a_column
251
+ def define_a_column(i)
252
+ case do_ocicall(@ctx) { @parms[i - 1].attrGet(OCI_ATTR_DATA_TYPE) }
253
+ when 8; @stmt.defineByPos(i, String, 65535) # Read LONG values
254
+ when 187; @stmt.defineByPos(i, OraDate) # Read TIMESTAMP values
255
+ when 108
256
+ if @parms[i - 1].attrGet(OCI_ATTR_TYPE_NAME) == 'XMLTYPE'
257
+ @stmt.defineByPos(i, String, 65535)
258
+ else
259
+ raise 'unsupported datatype'
260
+ end
261
+ else enhanced_define_a_column_pre_ar i
262
+ end
263
+ end
264
+ end
265
+ end
266
+
267
+ if OCI8.public_method_defined?(:describe_table)
268
+ # ruby-oci8 2.0 or upper
269
+
270
+ def describe(name)
271
+ info = describe_table(name.to_s)
272
+ raise %Q{"DESC #{name}" failed} if info.nil?
273
+ [info.obj_schema, info.obj_name]
274
+ end
275
+ else
276
+ # ruby-oci8 1.0.x or lower
277
+
278
+ # missing constant from oci8 < 0.1.14
279
+ OCI_PTYPE_UNK = 0 unless defined?(OCI_PTYPE_UNK)
280
+
281
+ # Uses the describeAny OCI call to find the target owner and table_name
282
+ # indicated by +name+, parsing through synonynms as necessary. Returns
283
+ # an array of [owner, table_name].
284
+ def describe(name)
285
+ @desc ||= @@env.alloc(OCIDescribe)
286
+ @desc.attrSet(OCI_ATTR_DESC_PUBLIC, -1) if VERSION >= '0.1.14'
287
+ do_ocicall(@ctx) { @desc.describeAny(@svc, name.to_s, OCI_PTYPE_UNK) } rescue raise %Q{"DESC #{name}" failed; does it exist?}
288
+ info = @desc.attrGet(OCI_ATTR_PARAM)
289
+
290
+ case info.attrGet(OCI_ATTR_PTYPE)
291
+ when OCI_PTYPE_TABLE, OCI_PTYPE_VIEW
292
+ owner = info.attrGet(OCI_ATTR_OBJ_SCHEMA)
293
+ table_name = info.attrGet(OCI_ATTR_OBJ_NAME)
294
+ [owner, table_name]
295
+ when OCI_PTYPE_SYN
296
+ schema = info.attrGet(OCI_ATTR_SCHEMA_NAME)
297
+ name = info.attrGet(OCI_ATTR_NAME)
298
+ describe(schema + '.' + name)
299
+ else raise %Q{"DESC #{name}" failed; not a table or view.}
300
+ end
301
+ end
302
+ end
303
+
304
+ end
305
+
306
+ # The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and
307
+ # reset functionality. If a call to #exec fails, and autocommit is turned on
308
+ # (ie., we're not in the middle of a longer transaction), it will
309
+ # automatically reconnect and try again. If autocommit is turned off,
310
+ # this would be dangerous (as the earlier part of the implied transaction
311
+ # may have failed silently if the connection died) -- so instead the
312
+ # connection is marked as dead, to be reconnected on it's next use.
313
+ #:stopdoc:
314
+ class OCI8EnhancedAutoRecover < DelegateClass(OCI8) #:nodoc:
315
+ attr_accessor :active #:nodoc:
316
+ alias :active? :active #:nodoc:
317
+
318
+ cattr_accessor :auto_retry
319
+ class << self
320
+ alias :auto_retry? :auto_retry #:nodoc:
321
+ end
322
+ @@auto_retry = false
323
+
324
+ def initialize(config, factory) #:nodoc:
325
+ @active = true
326
+ @config = config
327
+ @factory = factory
328
+ @connection = @factory.new_connection @config
329
+ super @connection
330
+ end
331
+
332
+ # Checks connection, returns true if active. Note that ping actively
333
+ # checks the connection, while #active? simply returns the last
334
+ # known state.
335
+ def ping #:nodoc:
336
+ @connection.exec("select 1 from dual") { |r| nil }
337
+ @active = true
338
+ rescue
339
+ @active = false
340
+ raise
341
+ end
342
+
343
+ # Resets connection, by logging off and creating a new connection.
344
+ def reset! #:nodoc:
345
+ logoff rescue nil
346
+ begin
347
+ @connection = @factory.new_connection @config
348
+ __setobj__ @connection
349
+ @active = true
350
+ rescue
351
+ @active = false
352
+ raise
353
+ end
354
+ end
355
+
356
+ # ORA-00028: your session has been killed
357
+ # ORA-01012: not logged on
358
+ # ORA-03113: end-of-file on communication channel
359
+ # ORA-03114: not connected to ORACLE
360
+ # ORA-03135: connection lost contact
361
+ LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114, 3135 ] #:nodoc:
362
+
363
+ # Adds auto-recovery functionality.
364
+ #
365
+ # See: http://www.jiubao.org/ruby-oci8/api.en.html#label-11
366
+ def exec(sql, *bindvars, &block) #:nodoc:
367
+ should_retry = self.class.auto_retry? && autocommit?
368
+
369
+ begin
370
+ @connection.exec(sql, *bindvars, &block)
371
+ rescue OCIException => e
372
+ raise unless e.is_a?(OCIError) && LOST_CONNECTION_ERROR_CODES.include?(e.code)
373
+ @active = false
374
+ raise unless should_retry
375
+ should_retry = false
376
+ reset! rescue nil
377
+ retry
378
+ end
379
+ end
380
+
381
+ # otherwise not working in Ruby 1.9.1
382
+ if RUBY_VERSION =~ /^1\.9/
383
+ def describe(name) #:nodoc:
384
+ @connection.describe(name)
385
+ end
386
+ end
387
+
388
+ end
389
+ #:startdoc:
@@ -0,0 +1,163 @@
1
+ # define accessors before requiring ruby-plsql as these accessors are used in clob writing callback and should be
2
+ # available also if ruby-plsql could not be loaded
3
+ ActiveRecord::Base.class_eval do
4
+ class_inheritable_accessor :custom_create_method, :custom_update_method, :custom_delete_method
5
+ end
6
+
7
+ require 'active_support'
8
+
9
+ module ActiveRecord #:nodoc:
10
+ module ConnectionAdapters #:nodoc:
11
+ module OracleEnhancedProcedures #:nodoc:
12
+
13
+ module ClassMethods
14
+ # Specify custom create method which should be used instead of Rails generated INSERT statement.
15
+ # Provided block should return ID of new record.
16
+ # Example:
17
+ # set_create_method do
18
+ # plsql.employees_pkg.create_employee(
19
+ # :p_first_name => first_name,
20
+ # :p_last_name => last_name,
21
+ # :p_employee_id => nil
22
+ # )[:p_employee_id]
23
+ # end
24
+ def set_create_method(&block)
25
+ include_with_custom_methods
26
+ self.custom_create_method = block
27
+ end
28
+
29
+ # Specify custom update method which should be used instead of Rails generated UPDATE statement.
30
+ # Example:
31
+ # set_update_method do
32
+ # plsql.employees_pkg.update_employee(
33
+ # :p_employee_id => id,
34
+ # :p_first_name => first_name,
35
+ # :p_last_name => last_name
36
+ # )
37
+ # end
38
+ def set_update_method(&block)
39
+ include_with_custom_methods
40
+ self.custom_update_method = block
41
+ end
42
+
43
+ # Specify custom delete method which should be used instead of Rails generated DELETE statement.
44
+ # Example:
45
+ # set_delete_method do
46
+ # plsql.employees_pkg.delete_employee(
47
+ # :p_employee_id => id
48
+ # )
49
+ # end
50
+ def set_delete_method(&block)
51
+ include_with_custom_methods
52
+ self.custom_delete_method = block
53
+ end
54
+
55
+ def create_method_name_before_custom_methods #:nodoc:
56
+ if private_method_defined?(:create_without_timestamps) && defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::STRING.to_f >= 2.3
57
+ :create_without_timestamps
58
+ elsif private_method_defined?(:create_without_callbacks)
59
+ :create_without_callbacks
60
+ else
61
+ :create
62
+ end
63
+ end
64
+
65
+ def update_method_name_before_custom_methods #:nodoc:
66
+ if private_method_defined?(:update_without_dirty)
67
+ :update_without_dirty
68
+ elsif private_method_defined?(:update_without_timestamps) && defined?(ActiveRecord::VERSION) && ActiveRecord::VERSION::STRING.to_f >= 2.3
69
+ :update_without_timestamps
70
+ elsif private_method_defined?(:update_without_callbacks)
71
+ :update_without_callbacks
72
+ else
73
+ :update
74
+ end
75
+ end
76
+
77
+ def destroy_method_name_before_custom_methods #:nodoc:
78
+ if public_method_defined?(:destroy_without_callbacks)
79
+ :destroy_without_callbacks
80
+ else
81
+ :destroy
82
+ end
83
+ end
84
+
85
+ private
86
+ def include_with_custom_methods
87
+ unless included_modules.include? InstanceMethods
88
+ include InstanceMethods
89
+ end
90
+ end
91
+ end
92
+
93
+ module InstanceMethods #:nodoc:
94
+ def self.included(base)
95
+ base.instance_eval do
96
+ alias_method :create_without_custom_method, create_method_name_before_custom_methods
97
+ alias_method create_method_name_before_custom_methods, :create_with_custom_method
98
+ alias_method :update_without_custom_method, update_method_name_before_custom_methods
99
+ alias_method update_method_name_before_custom_methods, :update_with_custom_method
100
+ alias_method :destroy_without_custom_method, destroy_method_name_before_custom_methods
101
+ alias_method destroy_method_name_before_custom_methods, :destroy_with_custom_method
102
+ private :create, :update
103
+ public :destroy
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ # Creates a record with custom create method
110
+ # and returns its id.
111
+ def create_with_custom_method
112
+ # check if class has custom create method
113
+ return create_without_custom_method unless self.class.custom_create_method
114
+ self.class.connection.log_custom_method("custom create method", "#{self.class.name} Create") do
115
+ self.id = self.class.custom_create_method.bind(self).call
116
+ end
117
+ @new_record = false
118
+ id
119
+ end
120
+
121
+ # Updates the associated record with custom update method
122
+ # Returns the number of affected rows.
123
+ def update_with_custom_method(attribute_names = @attributes.keys)
124
+ # check if class has custom create method
125
+ return update_without_custom_method unless self.class.custom_update_method
126
+ return 0 if attribute_names.empty?
127
+ self.class.connection.log_custom_method("custom update method with #{self.class.primary_key}=#{self.id}", "#{self.class.name} Update") do
128
+ self.class.custom_update_method.bind(self).call
129
+ end
130
+ 1
131
+ end
132
+
133
+ # Deletes the record in the database with custom delete method
134
+ # and freezes this instance to reflect that no changes should
135
+ # be made (since they can't be persisted).
136
+ def destroy_with_custom_method
137
+ # check if class has custom create method
138
+ return destroy_without_custom_method unless self.class.custom_delete_method
139
+ unless new_record?
140
+ self.class.connection.log_custom_method("custom delete method with #{self.class.primary_key}=#{self.id}", "#{self.class.name} Destroy") do
141
+ self.class.custom_delete_method.bind(self).call
142
+ end
143
+ end
144
+
145
+ @destroyed = true
146
+ freeze
147
+ end
148
+
149
+ end
150
+
151
+ end
152
+ end
153
+ end
154
+
155
+ ActiveRecord::Base.class_eval do
156
+ extend ActiveRecord::ConnectionAdapters::OracleEnhancedProcedures::ClassMethods
157
+ end
158
+
159
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
160
+ # public alias to log method which could be used from other objects
161
+ alias_method :log_custom_method, :log
162
+ public :log_custom_method
163
+ end