activerecord-oracle_enhanced-adapter 1.4.3 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: