aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/connection_adapters/oracle_adapter.rb')
-rw-r--r--activerecord/lib/active_record/connection_adapters/oracle_adapter.rb721
1 files changed, 0 insertions, 721 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb b/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb
deleted file mode 100644
index c862ebcb7c..0000000000
--- a/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb
+++ /dev/null
@@ -1,721 +0,0 @@
-# oracle_adapter.rb -- ActiveRecord adapter for Oracle 8i, 9i, 10g
-#
-# Original author: Graham Jenkins
-#
-# Current maintainer: Michael Schoen <schoenm@earthlink.net>
-#
-#########################################################################
-#
-# Implementation notes:
-# 1. Redefines (safely) a method in ActiveRecord to make it possible to
-# implement an autonumbering solution for Oracle.
-# 2. The OCI8 driver is patched to properly handle values for LONG and
-# TIMESTAMP columns. The driver-author has indicated that a future
-# release of the driver will obviate this patch.
-# 3. LOB support is implemented through an after_save callback.
-# 4. Oracle does not offer native LIMIT and OFFSET options; this
-# functionality is mimiced through the use of nested selects.
-# See http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
-#
-# Do what you want with this code, at your own peril, but if any
-# significant portion of my code remains then please acknowledge my
-# contribution.
-# portions Copyright 2005 Graham Jenkins
-
-require 'active_record/connection_adapters/abstract_adapter'
-require 'delegate'
-
-begin
- require_library_or_gem 'oci8' unless self.class.const_defined? :OCI8
-
- module ActiveRecord
- class Base
- def self.oracle_connection(config) #:nodoc:
- # Use OCI8AutoRecover instead of normal OCI8 driver.
- ConnectionAdapters::OracleAdapter.new OCI8AutoRecover.new(config), logger
- end
-
- # for backwards-compatibility
- def self.oci_connection(config) #:nodoc:
- config[:database] = config[:host]
- self.oracle_connection(config)
- end
-
- # After setting large objects to empty, select the OCI8::LOB
- # and write back the data.
- after_save :write_lobs
- def write_lobs #:nodoc:
- if connection.is_a?(ConnectionAdapters::OracleAdapter)
- connection.write_lobs(self.class.table_name, self.class, attributes)
- end
- end
-
- private :write_lobs
- end
-
-
- module ConnectionAdapters #:nodoc:
- class OracleColumn < Column #:nodoc:
-
- def type_cast(value)
- return guess_date_or_time(value) if type == :datetime && OracleAdapter.emulate_dates
- super
- end
-
- private
- def simplified_type(field_type)
- return :boolean if OracleAdapter.emulate_booleans && field_type == 'NUMBER(1)'
- case field_type
- when /date|time/i then :datetime
- else super
- end
- end
-
- def guess_date_or_time(value)
- (value.hour == 0 and value.min == 0 and value.sec == 0) ?
- Date.new(value.year, value.month, value.day) : value
- end
- end
-
-
- # This is an Oracle/OCI adapter for the ActiveRecord persistence
- # framework. It relies upon the OCI8 driver, which works with Oracle 8i
- # and above. Most recent development has been on Debian Linux against
- # a 10g database, ActiveRecord 1.12.1 and OCI8 0.1.13.
- # See: http://rubyforge.org/projects/ruby-oci8/
- #
- # Usage notes:
- # * Key generation assumes a "${table_name}_seq" sequence is available
- # for all tables; the sequence name can be changed using
- # ActiveRecord::Base.set_sequence_name. When using Migrations, these
- # sequences are created automatically.
- # * Oracle uses DATE or TIMESTAMP datatypes for both dates and times.
- # Consequently some hacks are employed to map data back to Date or Time
- # in Ruby. If the column_name ends in _time it's created as a Ruby Time.
- # Else if the hours/minutes/seconds are 0, I make it a Ruby Date. Else
- # it's a Ruby Time. This is a bit nasty - but if you use Duck Typing
- # you'll probably not care very much. In 9i and up it's tempting to
- # map DATE to Date and TIMESTAMP to Time, but too many databases use
- # DATE for both. Timezones and sub-second precision on timestamps are
- # not supported.
- # * Default values that are functions (such as "SYSDATE") are not
- # supported. This is a restriction of the way ActiveRecord supports
- # default values.
- # * Support for Oracle8 is limited by Rails' use of ANSI join syntax, which
- # is supported in Oracle9i and later. You will need to use #finder_sql for
- # has_and_belongs_to_many associations to run against Oracle8.
- #
- # Required parameters:
- #
- # * <tt>:username</tt>
- # * <tt>:password</tt>
- # * <tt>:database</tt>
- class OracleAdapter < AbstractAdapter
-
- @@emulate_booleans = true
- cattr_accessor :emulate_booleans
-
- @@emulate_dates = false
- cattr_accessor :emulate_dates
-
- def adapter_name #:nodoc:
- 'Oracle'
- end
-
- def supports_migrations? #:nodoc:
- true
- end
-
- def native_database_types #:nodoc:
- {
- :primary_key => "NUMBER(38) NOT NULL PRIMARY KEY",
- :string => { :name => "VARCHAR2", :limit => 255 },
- :text => { :name => "CLOB" },
- :integer => { :name => "NUMBER", :limit => 38 },
- :float => { :name => "NUMBER" },
- :decimal => { :name => "DECIMAL" },
- :datetime => { :name => "DATE" },
- :timestamp => { :name => "DATE" },
- :time => { :name => "DATE" },
- :date => { :name => "DATE" },
- :binary => { :name => "BLOB" },
- :boolean => { :name => "NUMBER", :limit => 1 }
- }
- end
-
- def table_alias_length
- 30
- end
-
- # QUOTING ==================================================
- #
- # see: abstract/quoting.rb
-
- # camelCase column names need to be quoted; not that anyone using Oracle
- # would really do this, but handling this case means we pass the test...
- def quote_column_name(name) #:nodoc:
- name.to_s =~ /[A-Z]/ ? "\"#{name}\"" : name
- end
-
- def quote_string(s) #:nodoc:
- s.gsub(/'/, "''")
- end
-
- def quote(value, column = nil) #:nodoc:
- if value && column && [:text, :binary].include?(column.type)
- %Q{empty_#{ column.sql_type.downcase rescue 'blob' }()}
- else
- super
- end
- end
-
- def quoted_true
- "1"
- end
-
- def quoted_false
- "0"
- end
-
-
- # CONNECTION MANAGEMENT ====================================
- #
-
- # Returns true if the connection is active.
- def active?
- # Pings the connection to check if it's still good. Note that an
- # #active? method is also available, but that simply returns the
- # last known state, which isn't good enough if the connection has
- # gone stale since the last use.
- @connection.ping
- rescue OCIException
- false
- end
-
- # Reconnects to the database.
- def reconnect!
- @connection.reset!
- rescue OCIException => e
- @logger.warn "#{adapter_name} automatic reconnection failed: #{e.message}"
- end
-
- # Disconnects from the database.
- def disconnect!
- @connection.logoff rescue nil
- @connection.active = false
- end
-
-
- # DATABASE STATEMENTS ======================================
- #
- # see: abstract/database_statements.rb
-
- def execute(sql, name = nil) #:nodoc:
- log(sql, name) { @connection.exec sql }
- end
-
- # Returns the next sequence value from a sequence generator. Not generally
- # called directly; used by ActiveRecord to get the next primary key value
- # when inserting a new database record (see #prefetch_primary_key?).
- def next_sequence_value(sequence_name)
- id = 0
- @connection.exec("select #{sequence_name}.nextval id from dual") { |r| id = r[0].to_i }
- id
- end
-
- def begin_db_transaction #:nodoc:
- @connection.autocommit = false
- end
-
- def commit_db_transaction #:nodoc:
- @connection.commit
- ensure
- @connection.autocommit = true
- end
-
- def rollback_db_transaction #:nodoc:
- @connection.rollback
- ensure
- @connection.autocommit = true
- end
-
- def add_limit_offset!(sql, options) #:nodoc:
- offset = options[:offset] || 0
-
- if limit = options[:limit]
- sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_ where rownum <= #{offset+limit}) where raw_rnum_ > #{offset}"
- elsif offset > 0
- sql.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{sql}) raw_sql_) where raw_rnum_ > #{offset}"
- end
- end
-
- # Returns true for Oracle adapter (since Oracle requires primary key
- # values to be pre-fetched before insert). See also #next_sequence_value.
- def prefetch_primary_key?(table_name = nil)
- true
- end
-
- def default_sequence_name(table, column) #:nodoc:
- "#{table}_seq"
- end
-
-
- # Inserts the given fixture into the table. Overridden to properly handle lobs.
- def insert_fixture(fixture, table_name)
- super
-
- klass = fixture.class_name.constantize rescue nil
- if klass.respond_to?(:ancestors) && klass.ancestors.include?(ActiveRecord::Base)
- write_lobs(table_name, klass, fixture)
- end
- end
-
- # Writes LOB values from attributes, as indicated by the LOB columns of klass.
- def write_lobs(table_name, klass, attributes)
- id = quote(attributes[klass.primary_key])
- klass.columns.select { |col| col.sql_type =~ /LOB$/i }.each do |col|
- value = attributes[col.name]
- value = value.to_yaml if col.text? && klass.serialized_attributes[col.name]
- next if value.nil? || (value == '')
- lob = select_one("SELECT #{col.name} FROM #{table_name} WHERE #{klass.primary_key} = #{id}",
- 'Writable Large Object')[col.name]
- lob.write value
- end
- end
-
-
- # SCHEMA STATEMENTS ========================================
- #
- # see: abstract/schema_statements.rb
-
- def current_database #:nodoc:
- select_one("select sys_context('userenv','db_name') db from dual")["db"]
- end
-
- def tables(name = nil) #:nodoc:
- select_all("select lower(table_name) from user_tables").inject([]) do | tabs, t |
- tabs << t.to_a.first.last
- end
- end
-
- def indexes(table_name, name = nil) #:nodoc:
- result = select_all(<<-SQL, name)
- SELECT lower(i.index_name) as index_name, i.uniqueness, lower(c.column_name) as column_name
- FROM user_indexes i, user_ind_columns c
- WHERE i.table_name = '#{table_name.to_s.upcase}'
- AND c.index_name = i.index_name
- AND i.index_name NOT IN (SELECT uc.index_name FROM user_constraints uc WHERE uc.constraint_type = 'P')
- ORDER BY i.index_name, c.column_position
- SQL
-
- current_index = nil
- indexes = []
-
- result.each do |row|
- if current_index != row['index_name']
- indexes << IndexDefinition.new(table_name, row['index_name'], row['uniqueness'] == "UNIQUE", [])
- current_index = row['index_name']
- end
-
- indexes.last.columns << row['column_name']
- end
-
- indexes
- end
-
- def columns(table_name, name = nil) #:nodoc:
- (owner, table_name) = @connection.describe(table_name)
-
- table_cols = <<-SQL
- select column_name as name, data_type as sql_type, data_default, nullable,
- decode(data_type, 'NUMBER', data_precision,
- 'FLOAT', data_precision,
- 'VARCHAR2', data_length,
- 'CHAR', data_length,
- null) as limit,
- decode(data_type, 'NUMBER', data_scale, null) as scale
- from all_tab_columns
- where owner = '#{owner}'
- and table_name = '#{table_name}'
- order by column_id
- SQL
-
- select_all(table_cols, name).map do |row|
- limit, scale = row['limit'], row['scale']
- if limit || scale
- row['sql_type'] << "(#{(limit || 38).to_i}" + ((scale = scale.to_i) > 0 ? ",#{scale})" : ")")
- end
-
- # clean up odd default spacing from Oracle
- if row['data_default']
- row['data_default'].sub!(/^(.*?)\s*$/, '\1')
- row['data_default'].sub!(/^'(.*)'$/, '\1')
- row['data_default'] = nil if row['data_default'] =~ /^(null|empty_[bc]lob\(\))$/i
- end
-
- OracleColumn.new(oracle_downcase(row['name']),
- row['data_default'],
- row['sql_type'],
- row['nullable'] == 'Y')
- end
- end
-
- def create_table(name, options = {}) #:nodoc:
- super(name, options)
- seq_name = options[:sequence_name] || "#{name}_seq"
- execute "CREATE SEQUENCE #{seq_name} START WITH 10000" unless options[:id] == false
- end
-
- def rename_table(name, new_name) #:nodoc:
- execute "RENAME #{name} TO #{new_name}"
- execute "RENAME #{name}_seq TO #{new_name}_seq" rescue nil
- end
-
- def drop_table(name, options = {}) #:nodoc:
- super(name)
- seq_name = options[:sequence_name] || "#{name}_seq"
- execute "DROP SEQUENCE #{seq_name}" rescue nil
- end
-
- def remove_index(table_name, options = {}) #:nodoc:
- execute "DROP INDEX #{index_name(table_name, options)}"
- end
-
- def change_column_default(table_name, column_name, default) #:nodoc:
- execute "ALTER TABLE #{table_name} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
- end
-
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
- change_column_sql = "ALTER TABLE #{table_name} MODIFY #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
- add_column_options!(change_column_sql, options)
- execute(change_column_sql)
- end
-
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
- execute "ALTER TABLE #{table_name} RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}"
- end
-
- def remove_column(table_name, column_name) #:nodoc:
- execute "ALTER TABLE #{table_name} DROP COLUMN #{quote_column_name(column_name)}"
- end
-
- # Find a table's primary key and sequence.
- # *Note*: Only primary key is implemented - sequence will be nil.
- def pk_and_sequence_for(table_name)
- (owner, table_name) = @connection.describe(table_name)
-
- pks = select_values(<<-SQL, 'Primary Key')
- select cc.column_name
- from all_constraints c, all_cons_columns cc
- where c.owner = '#{owner}'
- and c.table_name = '#{table_name}'
- and c.constraint_type = 'P'
- and cc.owner = c.owner
- and cc.constraint_name = c.constraint_name
- SQL
-
- # only support single column keys
- pks.size == 1 ? [oracle_downcase(pks.first), nil] : nil
- end
-
- def structure_dump #:nodoc:
- s = select_all("select sequence_name from user_sequences").inject("") do |structure, seq|
- structure << "create sequence #{seq.to_a.first.last};\n\n"
- end
-
- select_all("select table_name from user_tables").inject(s) do |structure, table|
- ddl = "create table #{table.to_a.first.last} (\n "
- cols = select_all(%Q{
- select column_name, data_type, data_length, data_precision, data_scale, data_default, nullable
- from user_tab_columns
- where table_name = '#{table.to_a.first.last}'
- order by column_id
- }).map do |row|
- col = "#{row['column_name'].downcase} #{row['data_type'].downcase}"
- if row['data_type'] =='NUMBER' and !row['data_precision'].nil?
- col << "(#{row['data_precision'].to_i}"
- col << ",#{row['data_scale'].to_i}" if !row['data_scale'].nil?
- col << ')'
- elsif row['data_type'].include?('CHAR')
- col << "(#{row['data_length'].to_i})"
- end
- col << " default #{row['data_default']}" if !row['data_default'].nil?
- col << ' not null' if row['nullable'] == 'N'
- col
- end
- ddl << cols.join(",\n ")
- ddl << ");\n\n"
- structure << ddl
- end
- end
-
- def structure_drop #:nodoc:
- s = select_all("select sequence_name from user_sequences").inject("") do |drop, seq|
- drop << "drop sequence #{seq.to_a.first.last};\n\n"
- end
-
- select_all("select table_name from user_tables").inject(s) do |drop, table|
- drop << "drop table #{table.to_a.first.last} cascade constraints;\n\n"
- end
- end
-
- def add_column_options!(sql, options) #:nodoc:
- # handle case of defaults for CLOB columns, which would otherwise get "quoted" incorrectly
- if options_include_default?(options) && (column = options[:column]) && column.type == :text
- sql << " DEFAULT #{quote(options.delete(:default))}"
- end
- super
- end
-
- # SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
- #
- # Oracle requires the ORDER BY columns to be in the SELECT list for DISTINCT
- # queries. However, with those columns included in the SELECT DISTINCT list, you
- # won't actually get a distinct list of the column you want (presuming the column
- # has duplicates with multiple values for the ordered-by columns. So we use the
- # FIRST_VALUE function to get a single (first) value for each column, effectively
- # making every row the same.
- #
- # distinct("posts.id", "posts.created_at desc")
- def distinct(columns, order_by)
- return "DISTINCT #{columns}" if order_by.blank?
-
- # construct a valid DISTINCT clause, ie. one that includes the ORDER BY columns, using
- # FIRST_VALUE such that the inclusion of these columns doesn't invalidate the DISTINCT
- order_columns = order_by.split(',').map { |s| s.strip }.reject(&:blank?)
- order_columns = order_columns.zip((0...order_columns.size).to_a).map do |c, i|
- "FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
- end
- sql = "DISTINCT #{columns}, "
- sql << order_columns * ", "
- end
-
- # ORDER BY clause for the passed order option.
- #
- # Uses column aliases as defined by #distinct.
- def add_order_by_for_association_limiting!(sql, options)
- return sql if options[:order].blank?
-
- order = options[:order].split(',').collect { |s| s.strip }.reject(&:blank?)
- order.map! {|s| $1 if s =~ / (.*)/}
- order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')
-
- sql << " ORDER BY #{order}"
- end
-
- private
-
- def select(sql, name = nil)
- cursor = execute(sql, name)
- cols = cursor.get_col_names.map { |x| oracle_downcase(x) }
- rows = []
-
- while row = cursor.fetch
- hash = Hash.new
-
- cols.each_with_index do |col, i|
- hash[col] =
- case row[i]
- when OCI8::LOB
- name == 'Writable Large Object' ? row[i]: row[i].read
- when OraDate
- d = row[i]
- if emulate_dates && (d.hour == 0 && d.minute == 0 && d.second == 0)
- d.to_date
- else
- # see string_to_time; Time overflowing to DateTime, respecting the default timezone
- time_array = [d.year, d.month, d.day, d.hour, d.minute, d.second]
- begin
- Time.send(Base.default_timezone, *time_array)
- rescue
- zone_offset = if Base.default_timezone == :local then DateTime.now.offset else 0 end
- # Append zero calendar reform start to account for dates skipped by calendar reform
- DateTime.new(*time_array[0..5] << zone_offset << 0) rescue nil
- end
- end
- else row[i]
- end unless col == 'raw_rnum_'
- end
-
- rows << hash
- end
-
- rows
- ensure
- cursor.close if cursor
- end
-
- # Oracle column names by default are case-insensitive, but treated as upcase;
- # for neatness, we'll downcase within Rails. EXCEPT that folks CAN quote
- # their column names when creating Oracle tables, which makes then case-sensitive.
- # I don't know anybody who does this, but we'll handle the theoretical case of a
- # camelCase column name. I imagine other dbs handle this different, since there's a
- # unit test that's currently failing test_oci.
- def oracle_downcase(column_name)
- column_name =~ /[a-z]/ ? column_name : column_name.downcase
- end
-
- end
- end
- end
-
-
- class OCI8 #:nodoc:
-
- # This OCI8 patch may not longer be required with the upcoming
- # release of version 0.2.
- class Cursor #:nodoc:
- alias :define_a_column_pre_ar :define_a_column
- def define_a_column(i)
- case do_ocicall(@ctx) { @parms[i - 1].attrGet(OCI_ATTR_DATA_TYPE) }
- when 8; @stmt.defineByPos(i, String, 65535) # Read LONG values
- when 187; @stmt.defineByPos(i, OraDate) # Read TIMESTAMP values
- when 108
- if @parms[i - 1].attrGet(OCI_ATTR_TYPE_NAME) == 'XMLTYPE'
- @stmt.defineByPos(i, String, 65535)
- else
- raise 'unsupported datatype'
- end
- else define_a_column_pre_ar i
- end
- end
- end
-
- # missing constant from oci8 < 0.1.14
- OCI_PTYPE_UNK = 0 unless defined?(OCI_PTYPE_UNK)
-
- # Uses the describeAny OCI call to find the target owner and table_name
- # indicated by +name+, parsing through synonynms as necessary. Returns
- # an array of [owner, table_name].
- def describe(name)
- @desc ||= @@env.alloc(OCIDescribe)
- @desc.attrSet(OCI_ATTR_DESC_PUBLIC, -1) if VERSION >= '0.1.14'
- @desc.describeAny(@svc, name.to_s, OCI_PTYPE_UNK) rescue raise %Q{"DESC #{name}" failed; does it exist?}
- info = @desc.attrGet(OCI_ATTR_PARAM)
-
- case info.attrGet(OCI_ATTR_PTYPE)
- when OCI_PTYPE_TABLE, OCI_PTYPE_VIEW
- owner = info.attrGet(OCI_ATTR_OBJ_SCHEMA)
- table_name = info.attrGet(OCI_ATTR_OBJ_NAME)
- [owner, table_name]
- when OCI_PTYPE_SYN
- schema = info.attrGet(OCI_ATTR_SCHEMA_NAME)
- name = info.attrGet(OCI_ATTR_NAME)
- describe(schema + '.' + name)
- else raise %Q{"DESC #{name}" failed; not a table or view.}
- end
- end
-
- end
-
-
- # The OracleConnectionFactory factors out the code necessary to connect and
- # configure an Oracle/OCI connection.
- class OracleConnectionFactory #:nodoc:
- def new_connection(username, password, database, async, prefetch_rows, cursor_sharing)
- conn = OCI8.new username, password, database
- conn.exec %q{alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'}
- conn.exec %q{alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS'} rescue nil
- conn.autocommit = true
- conn.non_blocking = true if async
- conn.prefetch_rows = prefetch_rows
- conn.exec "alter session set cursor_sharing = #{cursor_sharing}" rescue nil
- conn
- end
- end
-
-
- # The OCI8AutoRecover class enhances the OCI8 driver with auto-recover and
- # reset functionality. If a call to #exec fails, and autocommit is turned on
- # (ie., we're not in the middle of a longer transaction), it will
- # automatically reconnect and try again. If autocommit is turned off,
- # this would be dangerous (as the earlier part of the implied transaction
- # may have failed silently if the connection died) -- so instead the
- # connection is marked as dead, to be reconnected on it's next use.
- class OCI8AutoRecover < DelegateClass(OCI8) #:nodoc:
- attr_accessor :active
- alias :active? :active
-
- cattr_accessor :auto_retry
- class << self
- alias :auto_retry? :auto_retry
- end
- @@auto_retry = false
-
- def initialize(config, factory = OracleConnectionFactory.new)
- @active = true
- @username, @password, @database, = config[:username], config[:password], config[:database]
- @async = config[:allow_concurrency]
- @prefetch_rows = config[:prefetch_rows] || 100
- @cursor_sharing = config[:cursor_sharing] || 'similar'
- @factory = factory
- @connection = @factory.new_connection @username, @password, @database, @async, @prefetch_rows, @cursor_sharing
- super @connection
- end
-
- # Checks connection, returns true if active. Note that ping actively
- # checks the connection, while #active? simply returns the last
- # known state.
- def ping
- @connection.exec("select 1 from dual") { |r| nil }
- @active = true
- rescue
- @active = false
- raise
- end
-
- # Resets connection, by logging off and creating a new connection.
- def reset!
- logoff rescue nil
- begin
- @connection = @factory.new_connection @username, @password, @database, @async, @prefetch_rows, @cursor_sharing
- __setobj__ @connection
- @active = true
- rescue
- @active = false
- raise
- end
- end
-
- # ORA-00028: your session has been killed
- # ORA-01012: not logged on
- # ORA-03113: end-of-file on communication channel
- # ORA-03114: not connected to ORACLE
- LOST_CONNECTION_ERROR_CODES = [ 28, 1012, 3113, 3114 ]
-
- # Adds auto-recovery functionality.
- #
- # See: http://www.jiubao.org/ruby-oci8/api.en.html#label-11
- def exec(sql, *bindvars, &block)
- should_retry = self.class.auto_retry? && autocommit?
-
- begin
- @connection.exec(sql, *bindvars, &block)
- rescue OCIException => e
- raise unless LOST_CONNECTION_ERROR_CODES.include?(e.code)
- @active = false
- raise unless should_retry
- should_retry = false
- reset! rescue nil
- retry
- end
- end
-
- end
-
-rescue LoadError
- # OCI8 driver is unavailable.
- module ActiveRecord # :nodoc:
- class Base
- @@oracle_error_message = "Oracle/OCI libraries could not be loaded: #{$!.to_s}"
- def self.oracle_connection(config) # :nodoc:
- # Set up a reasonable error message
- raise LoadError, @@oracle_error_message
- end
- def self.oci_connection(config) # :nodoc:
- # Set up a reasonable error message
- raise LoadError, @@oracle_error_message
- end
- end
- end
-end