activerecord-oracle_enhanced-adapter 1.4.3 → 5.2.0

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 (100) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +1162 -2
  3. data/README.md +567 -155
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +3 -1
  6. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +19 -0
  7. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +132 -0
  8. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +345 -0
  9. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +52 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +280 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +64 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +59 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +538 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +38 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +46 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +435 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +44 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +196 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +164 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +95 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +79 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +194 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +709 -0
  24. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +28 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +353 -0
  26. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +33 -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 +385 -1083
  29. data/lib/active_record/type/oracle_enhanced/boolean.rb +20 -0
  30. data/lib/active_record/type/oracle_enhanced/integer.rb +15 -0
  31. data/lib/active_record/type/oracle_enhanced/json.rb +10 -0
  32. data/lib/active_record/type/oracle_enhanced/national_character_string.rb +26 -0
  33. data/lib/active_record/type/oracle_enhanced/national_character_text.rb +36 -0
  34. data/lib/active_record/type/oracle_enhanced/raw.rb +25 -0
  35. data/lib/active_record/type/oracle_enhanced/string.rb +29 -0
  36. data/lib/active_record/type/oracle_enhanced/text.rb +32 -0
  37. data/lib/active_record/type/oracle_enhanced/timestampltz.rb +25 -0
  38. data/lib/active_record/type/oracle_enhanced/timestamptz.rb +25 -0
  39. data/lib/activerecord-oracle_enhanced-adapter.rb +5 -13
  40. data/spec/active_record/connection_adapters/{oracle_enhanced_emulate_oracle_adapter_spec.rb → emulation/oracle_adapter_spec.rb} +5 -4
  41. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +469 -0
  42. data/spec/active_record/connection_adapters/{oracle_enhanced_context_index_spec.rb → oracle_enhanced/context_index_spec.rb} +140 -128
  43. data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +112 -0
  44. data/spec/active_record/connection_adapters/{oracle_enhanced_dbms_output_spec.rb → oracle_enhanced/dbms_output_spec.rb} +13 -13
  45. data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +365 -0
  46. data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +196 -0
  47. data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +492 -0
  48. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +1433 -0
  49. data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +478 -0
  50. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +385 -550
  51. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +92 -1249
  52. data/spec/active_record/oracle_enhanced/type/binary_spec.rb +119 -0
  53. data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +208 -0
  54. data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +139 -0
  55. data/spec/active_record/oracle_enhanced/type/float_spec.rb +48 -0
  56. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +91 -0
  57. data/spec/active_record/oracle_enhanced/type/json_spec.rb +57 -0
  58. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +55 -0
  59. data/spec/active_record/oracle_enhanced/type/national_character_text_spec.rb +230 -0
  60. data/spec/active_record/oracle_enhanced/type/raw_spec.rb +122 -0
  61. data/spec/active_record/oracle_enhanced/type/text_spec.rb +229 -0
  62. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +75 -0
  63. data/spec/spec_config.yaml.template +11 -0
  64. data/spec/spec_helper.rb +100 -93
  65. data/spec/support/alter_system_set_open_cursors.sql +1 -0
  66. data/spec/support/alter_system_user_password.sql +2 -0
  67. data/spec/support/create_oracle_enhanced_users.sql +31 -0
  68. metadata +105 -152
  69. data/.rspec +0 -2
  70. data/Gemfile +0 -52
  71. data/RUNNING_TESTS.md +0 -45
  72. data/Rakefile +0 -59
  73. data/activerecord-oracle_enhanced-adapter.gemspec +0 -130
  74. data/lib/active_record/connection_adapters/oracle_enhanced.rake +0 -105
  75. data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +0 -41
  76. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +0 -121
  77. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +0 -151
  78. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +0 -119
  79. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -359
  80. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +0 -25
  81. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +0 -21
  82. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -46
  83. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +0 -572
  84. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +0 -497
  85. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +0 -260
  86. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -227
  87. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +0 -260
  88. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -428
  89. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -258
  90. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +0 -294
  91. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +0 -17
  92. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
  93. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +0 -334
  94. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +0 -19
  95. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +0 -113
  96. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +0 -141
  97. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +0 -378
  98. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +0 -440
  99. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +0 -1400
  100. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +0 -339
@@ -1,497 +0,0 @@
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 raw_oci_connection
30
- if @raw_connection.is_a? OCI8
31
- @raw_connection
32
- # ActiveRecord Oracle enhanced adapter puts OCI8EnhancedAutoRecover wrapper around OCI8
33
- # in this case we need to pass original OCI8 connection
34
- else
35
- @raw_connection.instance_variable_get(:@connection)
36
- end
37
- end
38
-
39
- def auto_retry
40
- @raw_connection.auto_retry if @raw_connection
41
- end
42
-
43
- def auto_retry=(value)
44
- @raw_connection.auto_retry = value if @raw_connection
45
- end
46
-
47
- def logoff
48
- @raw_connection.logoff
49
- @raw_connection.active = false
50
- end
51
-
52
- def commit
53
- @raw_connection.commit
54
- end
55
-
56
- def rollback
57
- @raw_connection.rollback
58
- end
59
-
60
- def autocommit?
61
- @raw_connection.autocommit?
62
- end
63
-
64
- def autocommit=(value)
65
- @raw_connection.autocommit = value
66
- end
67
-
68
- # Checks connection, returns true if active. Note that ping actively
69
- # checks the connection, while #active? simply returns the last
70
- # known state.
71
- def ping
72
- @raw_connection.ping
73
- rescue OCIException => e
74
- raise OracleEnhancedConnectionException, e.message
75
- end
76
-
77
- def active?
78
- @raw_connection.active?
79
- end
80
-
81
- def reset!
82
- @raw_connection.reset!
83
- rescue OCIException => e
84
- raise OracleEnhancedConnectionException, e.message
85
- end
86
-
87
- def exec(sql, *bindvars, &block)
88
- @raw_connection.exec(sql, *bindvars, &block)
89
- end
90
-
91
- def returning_clause(quoted_pk)
92
- " RETURNING #{quoted_pk} INTO :insert_id"
93
- end
94
-
95
- # execute sql with RETURNING ... INTO :insert_id
96
- # and return :insert_id value
97
- def exec_with_returning(sql)
98
- cursor = @raw_connection.parse(sql)
99
- cursor.bind_param(':insert_id', nil, Integer)
100
- cursor.exec
101
- cursor[':insert_id']
102
- ensure
103
- cursor.close rescue nil
104
- end
105
-
106
- def prepare(sql)
107
- Cursor.new(self, @raw_connection.parse(sql))
108
- end
109
-
110
- class Cursor
111
- def initialize(connection, raw_cursor)
112
- @connection = connection
113
- @raw_cursor = raw_cursor
114
- end
115
-
116
- def bind_param(position, value, column = nil)
117
- if column && column.object_type?
118
- if @connection.raw_connection.respond_to? :get_tdo_by_typename
119
- @raw_cursor.bind_param(position, value, :named_type, column.sql_type)
120
- else
121
- raise "Use ruby-oci8 2.1.6 or later to bind Oracle objects."
122
- end
123
- elsif value.nil?
124
- @raw_cursor.bind_param(position, nil, String)
125
- else
126
- case col_type = column && column.type
127
- when :text, :binary
128
- # ruby-oci8 cannot create CLOB/BLOB from ''
129
- lob_value = value == '' ? ' ' : value
130
- bind_type = col_type == :text ? OCI8::CLOB : OCI8::BLOB
131
- ora_value = bind_type.new(@connection.raw_oci_connection, lob_value)
132
- ora_value.size = 0 if value == ''
133
- @raw_cursor.bind_param(position, ora_value)
134
- when :raw
135
- @raw_cursor.bind_param(position, OracleEnhancedAdapter.encode_raw(value))
136
- when :decimal
137
- @raw_cursor.bind_param(position, BigDecimal.new(value.to_s))
138
- else
139
- @raw_cursor.bind_param(position, value)
140
- end
141
- end
142
- end
143
-
144
- def bind_returning_param(position, bind_type)
145
- @raw_cursor.bind_param(position, nil, bind_type)
146
- end
147
-
148
- def exec
149
- @raw_cursor.exec
150
- end
151
-
152
- def exec_update
153
- @raw_cursor.exec
154
- end
155
-
156
- def get_col_names
157
- @raw_cursor.get_col_names
158
- end
159
-
160
- def fetch(options={})
161
- if row = @raw_cursor.fetch
162
- get_lob_value = options[:get_lob_value]
163
- row.map do |col|
164
- @connection.typecast_result_value(col, get_lob_value)
165
- end
166
- end
167
- end
168
-
169
- def get_returning_param(position, type)
170
- @raw_cursor[position]
171
- end
172
-
173
- def close
174
- @raw_cursor.close
175
- end
176
-
177
- end
178
-
179
- def select(sql, name = nil, return_column_names = false)
180
- cursor = @raw_connection.exec(sql)
181
- cols = []
182
- # Ignore raw_rnum_ which is used to simulate LIMIT and OFFSET
183
- cursor.get_col_names.each do |col_name|
184
- col_name = oracle_downcase(col_name)
185
- cols << col_name unless col_name == 'raw_rnum_'
186
- end
187
- # Reuse the same hash for all rows
188
- column_hash = {}
189
- cols.each {|c| column_hash[c] = nil}
190
- rows = []
191
- get_lob_value = !(name == 'Writable Large Object')
192
-
193
- while row = cursor.fetch
194
- hash = column_hash.dup
195
-
196
- cols.each_with_index do |col, i|
197
- hash[col] = typecast_result_value(row[i], get_lob_value)
198
- end
199
-
200
- rows << hash
201
- end
202
-
203
- return_column_names ? [rows, cols] : rows
204
- ensure
205
- cursor.close if cursor
206
- end
207
-
208
- def write_lob(lob, value, is_binary = false)
209
- lob.write value
210
- end
211
-
212
- def describe(name)
213
- # fall back to SELECT based describe if using database link
214
- return super if name.to_s.include?('@')
215
- quoted_name = OracleEnhancedAdapter.valid_table_name?(name) ? name : "\"#{name}\""
216
- @raw_connection.describe(quoted_name)
217
- rescue OCIException => e
218
- # fall back to SELECT which can handle synonyms to database links
219
- super
220
- end
221
-
222
- # Return OCIError error code
223
- def error_code(exception)
224
- case exception
225
- when OCIError
226
- exception.code
227
- else
228
- nil
229
- end
230
- end
231
-
232
- def typecast_result_value(value, get_lob_value)
233
- case value
234
- when Fixnum, Bignum
235
- value
236
- when String
237
- value
238
- when Float, BigDecimal
239
- # return Fixnum or Bignum if value is integer (to avoid issues with _before_type_cast values for id attributes)
240
- value == (v_to_i = value.to_i) ? v_to_i : value
241
- when OraNumber
242
- # change OraNumber value (returned in early versions of ruby-oci8 2.0.x) to BigDecimal
243
- value == (v_to_i = value.to_i) ? v_to_i : BigDecimal.new(value.to_s)
244
- when OCI8::LOB
245
- if get_lob_value
246
- data = value.read || "" # if value.read returns nil, then we have an empty_clob() i.e. an empty string
247
- # In Ruby 1.9.1 always change encoding to ASCII-8BIT for binaries
248
- data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding) && value.is_a?(OCI8::BLOB)
249
- data
250
- else
251
- value
252
- end
253
- # ruby-oci8 1.0 returns OraDate
254
- # ruby-oci8 2.0 returns Time or DateTime
255
- when OraDate, Time, DateTime
256
- if OracleEnhancedAdapter.emulate_dates && date_without_time?(value)
257
- value.to_date
258
- else
259
- create_time_with_default_timezone(value)
260
- end
261
- else
262
- value
263
- end
264
- end
265
-
266
- private
267
-
268
- def date_without_time?(value)
269
- case value
270
- when OraDate
271
- value.hour == 0 && value.minute == 0 && value.second == 0
272
- else
273
- value.hour == 0 && value.min == 0 && value.sec == 0
274
- end
275
- end
276
-
277
- def create_time_with_default_timezone(value)
278
- year, month, day, hour, min, sec, usec = case value
279
- when Time
280
- [value.year, value.month, value.day, value.hour, value.min, value.sec, value.usec]
281
- when OraDate
282
- [value.year, value.month, value.day, value.hour, value.minute, value.second, 0]
283
- else
284
- [value.year, value.month, value.day, value.hour, value.min, value.sec, 0]
285
- end
286
- # code from Time.time_with_datetime_fallback
287
- begin
288
- Time.send(Base.default_timezone, year, month, day, hour, min, sec, usec)
289
- rescue
290
- offset = Base.default_timezone.to_sym == :local ? ::DateTime.local_offset : 0
291
- ::DateTime.civil(year, month, day, hour, min, sec, offset)
292
- end
293
- end
294
-
295
- end
296
-
297
- # The OracleEnhancedOCIFactory factors out the code necessary to connect and
298
- # configure an Oracle/OCI connection.
299
- class OracleEnhancedOCIFactory #:nodoc:
300
- def self.new_connection(config)
301
- # to_s needed if username, password or database is specified as number in database.yml file
302
- username = config[:username] && config[:username].to_s
303
- password = config[:password] && config[:password].to_s
304
- database = config[:database] && config[:database].to_s
305
- host, port = config[:host], config[:port]
306
- privilege = config[:privilege] && config[:privilege].to_sym
307
- async = config[:allow_concurrency]
308
- prefetch_rows = config[:prefetch_rows] || 100
309
- cursor_sharing = config[:cursor_sharing] || 'force'
310
- # get session time_zone from configuration or from TZ environment variable
311
- time_zone = config[:time_zone] || ENV['TZ']
312
-
313
- # connection using host, port and database name
314
- connection_string = if host || port
315
- host ||= 'localhost'
316
- host = "[#{host}]" if host =~ /^[^\[].*:/ # IPv6
317
- port ||= 1521
318
- database = "/#{database}" unless database.match(/^\//)
319
- "//#{host}:#{port}#{database}"
320
- # if no host is specified then assume that
321
- # database parameter is TNS alias or TNS connection string
322
- else
323
- database
324
- end
325
-
326
- conn = OCI8.new username, password, connection_string, privilege
327
- conn.autocommit = true
328
- conn.non_blocking = true if async
329
- conn.prefetch_rows = prefetch_rows
330
- conn.exec "alter session set cursor_sharing = #{cursor_sharing}" rescue nil
331
- conn.exec "alter session set time_zone = '#{time_zone}'" unless time_zone.blank?
332
-
333
- # Initialize NLS parameters
334
- OracleEnhancedAdapter::DEFAULT_NLS_PARAMETERS.each do |key, default_value|
335
- value = config[key] || ENV[key.to_s.upcase] || default_value
336
- if value
337
- conn.exec "alter session set #{key} = '#{value}'"
338
- end
339
- end
340
- conn
341
- end
342
- end
343
-
344
-
345
- end
346
- end
347
-
348
-
349
-
350
- class OCI8 #:nodoc:
351
-
352
- class Cursor #:nodoc:
353
- if method_defined? :define_a_column
354
- # This OCI8 patch is required with the ruby-oci8 1.0.x or lower.
355
- # Set OCI8::BindType::Mapping[] to change the column type
356
- # when using ruby-oci8 2.0.
357
-
358
- alias :enhanced_define_a_column_pre_ar :define_a_column
359
- def define_a_column(i)
360
- case do_ocicall(@ctx) { @parms[i - 1].attrGet(OCI_ATTR_DATA_TYPE) }
361
- when 8; @stmt.defineByPos(i, String, 65535) # Read LONG values
362
- when 187; @stmt.defineByPos(i, OraDate) # Read TIMESTAMP values
363
- when 108
364
- if @parms[i - 1].attrGet(OCI_ATTR_TYPE_NAME) == 'XMLTYPE'
365
- @stmt.defineByPos(i, String, 65535)
366
- else
367
- raise 'unsupported datatype'
368
- end
369
- else enhanced_define_a_column_pre_ar i
370
- end
371
- end
372
- end
373
- end
374
-
375
- if OCI8.public_method_defined?(:describe_table)
376
- # ruby-oci8 2.0 or upper
377
-
378
- def describe(name)
379
- info = describe_table(name.to_s)
380
- raise %Q{"DESC #{name}" failed} if info.nil?
381
- [info.obj_schema, info.obj_name]
382
- end
383
- else
384
- # ruby-oci8 1.0.x or lower
385
-
386
- # missing constant from oci8 < 0.1.14
387
- OCI_PTYPE_UNK = 0 unless defined?(OCI_PTYPE_UNK)
388
-
389
- # Uses the describeAny OCI call to find the target owner and table_name
390
- # indicated by +name+, parsing through synonynms as necessary. Returns
391
- # an array of [owner, table_name].
392
- def describe(name)
393
- @desc ||= @@env.alloc(OCIDescribe)
394
- @desc.attrSet(OCI_ATTR_DESC_PUBLIC, -1) if VERSION >= '0.1.14'
395
- do_ocicall(@ctx) { @desc.describeAny(@svc, name.to_s, OCI_PTYPE_UNK) } rescue raise %Q{"DESC #{name}" failed; does it exist?}
396
- info = @desc.attrGet(OCI_ATTR_PARAM)
397
-
398
- case info.attrGet(OCI_ATTR_PTYPE)
399
- when OCI_PTYPE_TABLE, OCI_PTYPE_VIEW
400
- owner = info.attrGet(OCI_ATTR_OBJ_SCHEMA)
401
- table_name = info.attrGet(OCI_ATTR_OBJ_NAME)
402
- [owner, table_name]
403
- when OCI_PTYPE_SYN
404
- schema = info.attrGet(OCI_ATTR_SCHEMA_NAME)
405
- name = info.attrGet(OCI_ATTR_NAME)
406
- describe(schema + '.' + name)
407
- else raise %Q{"DESC #{name}" failed; not a table or view.}
408
- end
409
- end
410
- end
411
-
412
- end
413
-
414
- # The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and
415
- # reset functionality. If a call to #exec fails, and autocommit is turned on
416
- # (ie., we're not in the middle of a longer transaction), it will
417
- # automatically reconnect and try again. If autocommit is turned off,
418
- # this would be dangerous (as the earlier part of the implied transaction
419
- # may have failed silently if the connection died) -- so instead the
420
- # connection is marked as dead, to be reconnected on it's next use.
421
- #:stopdoc:
422
- class OCI8EnhancedAutoRecover < DelegateClass(OCI8) #:nodoc:
423
- attr_accessor :active #:nodoc:
424
- alias :active? :active #:nodoc:
425
-
426
- cattr_accessor :auto_retry
427
- class << self
428
- alias :auto_retry? :auto_retry #:nodoc:
429
- end
430
- @@auto_retry = false
431
-
432
- def initialize(config, factory) #:nodoc:
433
- @active = true
434
- @config = config
435
- @factory = factory
436
- @connection = @factory.new_connection @config
437
- super @connection
438
- end
439
-
440
- # Checks connection, returns true if active. Note that ping actively
441
- # checks the connection, while #active? simply returns the last
442
- # known state.
443
- def ping #:nodoc:
444
- @connection.exec("select 1 from dual") { |r| nil }
445
- @active = true
446
- rescue
447
- @active = false
448
- raise
449
- end
450
-
451
- # Resets connection, by logging off and creating a new connection.
452
- def reset! #:nodoc:
453
- logoff rescue nil
454
- begin
455
- @connection = @factory.new_connection @config
456
- __setobj__ @connection
457
- @active = true
458
- rescue
459
- @active = false
460
- raise
461
- end
462
- end
463
-
464
- # ORA-00028: your session has been killed
465
- # ORA-01012: not logged on
466
- # ORA-03113: end-of-file on communication channel
467
- # ORA-03114: not connected to ORACLE
468
- # ORA-03135: connection lost contact
469
- LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114, 3135 ] #:nodoc:
470
-
471
- # Adds auto-recovery functionality.
472
- #
473
- # See: http://www.jiubao.org/ruby-oci8/api.en.html#label-11
474
- def exec(sql, *bindvars, &block) #:nodoc:
475
- should_retry = self.class.auto_retry? && autocommit?
476
-
477
- begin
478
- @connection.exec(sql, *bindvars, &block)
479
- rescue OCIException => e
480
- raise unless e.is_a?(OCIError) && LOST_CONNECTION_ERROR_CODES.include?(e.code)
481
- @active = false
482
- raise unless should_retry
483
- should_retry = false
484
- reset! rescue nil
485
- retry
486
- end
487
- end
488
-
489
- # otherwise not working in Ruby 1.9.1
490
- if RUBY_VERSION =~ /^1\.9/
491
- def describe(name) #:nodoc:
492
- @connection.describe(name)
493
- end
494
- end
495
-
496
- end
497
- #:startdoc: