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: 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:

  • :username

  • :password

  • :database

Methods
A
D
N
P
Q
R
T
Instance Public methods
active?()

Returns true if the connection is active.

# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 193
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
add_order_by_for_association_limiting!(sql, options)

ORDER BY clause for the passed order option.

Uses column aliases as defined by distinct.

# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 477
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 =~ %r (.*)/}
  order = order.zip((0...order.size).to_a).map { |s,i| "alias_#{i}__ #{s}" }.join(', ')

  sql << "ORDER BY #{order}"
end
disconnect!()

Disconnects from the database.

# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 211
def disconnect!
  @connection.logoff rescue nil
  @connection.active = false
end
distinct(columns, order_by)

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")
# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 461
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
next_sequence_value(sequence_name)

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?).

# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 228
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
pk_and_sequence_for(table_name)

Find a table’s primary key and sequence. Note: Only primary key is implemented - sequence will be nil.

# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 393
        def pk_and_sequence_for(table_name)
          (owner, table_name) = @connection.describe(table_name)

          pks = select_values("            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
", 'Primary Key')

          # only support single column keys
          pks.size == 1 ? [oracle_downcase(pks.first), nil] : nil
        end
prefetch_primary_key?(table_name = nil)

Returns true for Oracle adapter (since Oracle requires primary key values to be pre-fetched before insert). See also next_sequence_value.

# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 267
def prefetch_primary_key?(table_name = nil)
  true
end
quoted_false()
# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 184
def quoted_false
  "0"
end
quoted_true()
# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 180
def quoted_true
  "1"
end
reconnect!()

Reconnects to the database.

# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 204
def reconnect!
  @connection.reset!
rescue OCIException => e
  @logger.warn "#{adapter_name} automatic reconnection failed: #{e.message}"
end
table_alias_length()
# File rails/activerecord/lib/active_record/connection_adapters/oracle_adapter.rb, line 154
def table_alias_length
  30
end