unclebilly-activerecord-oracle_enhanced-adapter 1.2.4

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