module ArJdbc::Oracle

Constants

ADAPTER_NAME
AUTOGENERATED_SEQUENCE_NAME

use in set_sequence_name to avoid fetching primary key value from sequence

IDENTIFIER_LENGTH
LOWER_CASE_ONLY

@private

NATIVE_DATABASE_TYPES

Public Class Methods

arel_visitor_type(config = nil) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 126
def self.arel_visitor_type(config = nil)
  ::Arel::Visitors::Oracle
end
column_selector() click to toggle source

@see ActiveRecord::ConnectionAdapters::JdbcColumn#column_types

# File lib/arjdbc/oracle/column.rb, line 5
def self.column_selector
  [ /oracle/i, lambda { |config, column| column.extend(Column) } ]
end
emulate_booleans() click to toggle source

@deprecated Use {#emulate_booleans?} instead.

# File lib/arjdbc/oracle/adapter.rb, line 89
def self.emulate_booleans; @@emulate_booleans; end
emulate_booleans=(emulate) click to toggle source

@see emulate_booleans?

# File lib/arjdbc/oracle/adapter.rb, line 91
def self.emulate_booleans=(emulate); @@emulate_booleans = emulate; end
emulate_booleans?() click to toggle source

Boolean emulation can be disabled using :

ArJdbc::Oracle.emulate_booleans = false

@see ActiveRecord::ConnectionAdapters::OracleAdapter#emulate_booleans

# File lib/arjdbc/oracle/adapter.rb, line 87
def self.emulate_booleans?; @@emulate_booleans; end
extended(adapter) click to toggle source

@private

# File lib/arjdbc/oracle/adapter.rb, line 33
def self.extended(adapter); initialize!; end
initialize!() click to toggle source

@private

# File lib/arjdbc/oracle/adapter.rb, line 39
def self.initialize!
  return if @@_initialized; @@_initialized = true

  require 'arjdbc/util/serialized_attributes'
  Util::SerializedAttributes.setup /LOB\(|LOB$/i, 'after_save_with_oracle_lob'

  unless ActiveRecord::ConnectionAdapters::AbstractAdapter.
      instance_methods(false).detect { |m| m.to_s == "prefetch_primary_key?" }
    require 'arjdbc/jdbc/quoted_primary_key'
    ActiveRecord::Base.extend ArJdbc::QuotedPrimaryKeyExtension
  end
end
jdbc_connection_class() click to toggle source

@see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_connection_class

# File lib/arjdbc/oracle/adapter.rb, line 53
def self.jdbc_connection_class
  ::ActiveRecord::ConnectionAdapters::OracleJdbcConnection
end
update_lob_values=(update) click to toggle source

@see update_lob_values?

# File lib/arjdbc/oracle/adapter.rb, line 71
def self.update_lob_values=(update); @@update_lob_values = update; end
update_lob_values?() click to toggle source

Updating records with LOB values (binary/text columns) in a separate statement can be disabled using :

ArJdbc::Oracle.update_lob_values = false

@note This only applies when prepared statements are not used.

# File lib/arjdbc/oracle/adapter.rb, line 69
def self.update_lob_values?; @@update_lob_values; end

Public Instance Methods

adapter_name() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 138
def adapter_name
  ADAPTER_NAME
end
add_column_options!(sql, options) click to toggle source

@override

Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 416
def add_column_options!(sql, options)
  # 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
add_index(table_name, column_name, options = {}) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 354
def add_index(table_name, column_name, options = {})
  index_name, index_type, quoted_column_names, tablespace, index_options = add_index_options(table_name, column_name, options)
  execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{index_options}"
  if index_type == 'UNIQUE'
    unless quoted_column_names =~ /\(.*\)/
      execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
    end
  end
end
add_index_options(table_name, column_name, options = {}) click to toggle source

@private

# File lib/arjdbc/oracle/adapter.rb, line 365
def add_index_options(table_name, column_name, options = {})
  column_names = Array(column_name)
  index_name   = index_name(table_name, column: column_names)

  options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)

  index_type = options[:unique] ? "UNIQUE" : ""
  index_name = options[:name].to_s if options.key?(:name)
  tablespace = '' # tablespace_for(:index, options[:tablespace])
  max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
  index_options =  '' # index_options = options[:options]

  if index_name.to_s.length > max_index_length
    raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
  end
  if index_name_exists?(table_name, index_name, false)
    raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
  end

  quoted_column_names = column_names.map { |e| quote_column_name(e, true) }.join(", ")
  [ index_name, index_type, quoted_column_names, tablespace, index_options ]
end
add_limit_offset!(sql, options) click to toggle source

@note Only used with (non-AREL) ActiveRecord *2.3*. @see Arel::Visitors::Oracle

# File lib/arjdbc/oracle/adapter.rb, line 319
def add_limit_offset!(sql, options)
  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
add_order_by_for_association_limiting!(sql, options) click to toggle source

ORDER BY clause for the passed order option.

Uses column aliases as defined by {#distinct}.

# File lib/arjdbc/oracle/adapter.rb, line 498
def add_order_by_for_association_limiting!(sql, options)
  return sql if options[:order].blank?

  order_columns = extract_order_columns(options[:order]) do |columns|
    columns.map! { |s| $1 if s =~ / (.*)/ }; columns
  end
  order = order_columns.map { |s, i| "alias_#{i}__ #{s}" } # @see {#distinct}

  sql << "ORDER BY #{order.join(', ')}"
end
change_column(table_name, column_name, type, options = {}) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 425
def change_column(table_name, column_name, type, options = {})
  change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} " <<
    "MODIFY #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit])}"
  add_column_options!(change_column_sql, options)
  execute(change_column_sql)
end
change_column_default(table_name, column_name, default) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 411
def change_column_default(table_name, column_name, default)
  execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
end
charset() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 534
def charset
  database_parameters['NLS_CHARACTERSET']
end
clear_cache!() click to toggle source
Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 167
def clear_cache!
  super
  reload_type_map
end
clear_prefetch_primary_key() click to toggle source

used to clear prefetch primary key flag for all tables @private

# File lib/arjdbc/oracle/adapter.rb, line 705
def clear_prefetch_primary_key; @@do_not_prefetch_primary_key = {} end
collation() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 538
def collation
  database_parameters['NLS_COMP']
end
column_name_length() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 240
def column_name_length; IDENTIFIER_LENGTH; end
columns(table_name, name = nil) click to toggle source

NOTE: better to use #current_schema instead of the configured one ?!

# File lib/arjdbc/oracle/adapter.rb, line 526
def columns(table_name, name = nil)
  @connection.columns_internal(table_name.to_s, nil, oracle_schema)
end
columns_for_distinct(columns, orders) click to toggle source

@override Since AR 4.0 (on 4.1 {#distinct} is gone and won't be called).

# File lib/arjdbc/oracle/adapter.rb, line 479
def columns_for_distinct(columns, orders)
  return columns if orders.blank?
  if orders.is_a?(Array) # AR 3.x vs 4.x
    orders = orders.map { |column| column.is_a?(String) ? column : column.to_sql }
  else
    orders = extract_order_columns(orders)
  end
  # 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 = orders.map do |c, i|
    "FIRST_VALUE(#{c.split.first}) OVER (PARTITION BY #{columns} ORDER BY #{c}) AS alias_#{i}__"
  end
  columns = [ columns ]; columns.flatten!
  columns.push( *order_columns ).join(', ')
end
create_table(name, options = {}) click to toggle source

@override

Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 256
def create_table(name, options = {})
  super(name, options)
  unless options[:id] == false
    seq_name = options[:sequence_name] || default_sequence_name(name)
    start_value = options[:sequence_start_value] || 10000
    raise ActiveRecord::StatementInvalid.new("name #{seq_name} too long") if seq_name.length > table_alias_length
    execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{start_value}"
  end
end
create_table_definition(name, temporary, options, as = nil) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 122
def create_table_definition(name, temporary, options, as = nil)
  TableDefinition.new native_database_types, name, temporary, options, as
end
current_database() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 336
def current_database
  @current_database ||= execute("SELECT sys_context('userenv', 'db_name') db FROM dual").first['db']
end
current_schema() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 340
def current_schema
  execute("SELECT sys_context('userenv', 'current_schema') schema FROM dual").first['schema']
end
current_schema=(schema_owner) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 344
def current_schema=(schema_owner)
  execute("ALTER SESSION SET current_schema=#{schema_owner}")
end
current_user() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 332
def current_user
  @current_user ||= execute("SELECT sys_context('userenv', 'session_user') su FROM dual").first['su']
end
database_parameters() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 542
def database_parameters
  return @database_parameters unless ( @database_parameters ||= {} ).empty?
  @connection.execute_query_raw("SELECT * FROM NLS_DATABASE_PARAMETERS") do
    |name, value| @database_parameters[name] = value
  end
  @database_parameters
end
default_sequence_name(table_name, primary_key = nil) click to toggle source

@private Will take all or first 26 characters of table name and append _seq suffix

# File lib/arjdbc/oracle/adapter.rb, line 245
def default_sequence_name(table_name, primary_key = nil)
  len = IDENTIFIER_LENGTH - 4
  table_name.to_s.gsub (/(^|\.)([\w$-]{1,#{len}})([\w$-]*)$/), '\1\2_seq'
end
default_trigger_name(table_name) click to toggle source

@private

# File lib/arjdbc/oracle/adapter.rb, line 251
def default_trigger_name(table_name)
  "#{table_name.to_s[0, IDENTIFIER_LENGTH - 4]}_pkt"
end
disable_referential_integrity() { || ... } click to toggle source

@private

# File lib/arjdbc/oracle/adapter.rb, line 773
def disable_referential_integrity
  sql_constraints = "SELECT constraint_name, owner, table_name FROM user_constraints WHERE constraint_type = 'R' AND status = 'ENABLED'"
  old_constraints = select_all(sql_constraints)
  begin
    old_constraints.each do |constraint|
      execute "ALTER TABLE #{constraint["table_name"]} DISABLE CONSTRAINT #{constraint["constraint_name"]}"
    end
    yield
  ensure
    old_constraints.reverse_each do |constraint|
      execute "ALTER TABLE #{constraint["table_name"]} ENABLE CONSTRAINT #{constraint["constraint_name"]}"
    end
  end
end
distinct(columns, order_by) click to toggle source

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")

@override

# File lib/arjdbc/oracle/adapter.rb, line 474
def distinct(columns, order_by)
  "DISTINCT #{columns_for_distinct(columns, order_by)}"
end
drop_table(name, options = {}) click to toggle source

@override

Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 279
def drop_table(name, options = {})
  outcome = super(name)
  return outcome if name == 'schema_migrations'
  seq_name = options.key?(:sequence_name) ? # pass nil/false - no sequence
    options[:sequence_name] : default_sequence_name(name)
  return outcome unless seq_name
  execute_quietly "DROP SEQUENCE #{quote_table_name(seq_name)}"
end
exec_insert(sql, name, binds, pk = nil, sequence_name = nil) click to toggle source

@override

Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 830
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
  if pk && use_insert_returning?
    if sql.is_a?(String) && sql.index('RETURNING')
      return exec_insert_returning(sql, name, binds, pk)
    end
  end
  super(sql, name, binds) # assume no generated id for table
end
exec_insert_returning(sql, name, binds, pk = nil) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 839
def exec_insert_returning(sql, name, binds, pk = nil)
  sql = to_sql(sql, binds) if sql.respond_to?(:to_sql)
  if prepared_statements?
    log(sql, name, binds) { @connection.execute_insert_returning(sql, binds) }
  else
    log(sql, name) { @connection.execute_insert_returning(sql, nil) }
  end
end
explain(arel, binds = []) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 670
def explain(arel, binds = [])
  sql = "EXPLAIN PLAN FOR #{to_sql(arel, binds)}"
  return if sql =~ /FROM all_/
  exec_update(sql, 'EXPLAIN', binds)
  select_values("SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY)", 'EXPLAIN').join("\n")
end
has_primary_key?(table_name, owner = nil, desc_table_name = nil, db_link = nil) click to toggle source

@private

# File lib/arjdbc/oracle/adapter.rb, line 708
def has_primary_key?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
  ! pk_and_sequence_for(table_name, owner, desc_table_name, db_link).nil?
end
has_primary_key_trigger?(table_name, owner = nil, desc_table_name = nil, db_link = nil) click to toggle source

@private check if table has primary key trigger with _pkt suffix

# File lib/arjdbc/oracle/adapter.rb, line 713
def has_primary_key_trigger?(table_name, owner = nil, desc_table_name = nil, db_link = nil)
  (owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name

  trigger_name = default_trigger_name(table_name).upcase
  pkt_sql = "SELECT trigger_name FROM all_triggers#{db_link} WHERE owner = '#{owner}'" <<
      " AND trigger_name = '#{trigger_name}'" <<
      " AND table_owner = '#{owner}'" <<
      " AND table_name = '#{desc_table_name}'" <<
      " AND status = 'ENABLED'"
  select_value(pkt_sql, 'Primary Key Trigger') ? true : false
end
ids_in_list_limit()
Alias for: in_clause_length
in_clause_length() click to toggle source

Prevent ORA-01795 for in clauses with more than 1000

# File lib/arjdbc/oracle/adapter.rb, line 229
def in_clause_length
  1000
end
Also aliased as: ids_in_list_limit
index_name_length() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 239
def index_name_length;  IDENTIFIER_LENGTH; end
indexes(table, name = nil) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 313
def indexes(table, name = nil)
  @connection.indexes(table, name, schema_owner)
end
initialize_type_map(m) click to toggle source
Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 142
def initialize_type_map(m)
  super

  m.register_type(%r(NUMBER)i) do |sql_type|
    scale = extract_scale(sql_type)
    precision = extract_precision(sql_type)
    limit = extract_limit(sql_type)
    if scale == 0
      if Oracle.emulate_booleans? && limit == 1
        ActiveRecord::Type::Boolean.new
      else
        ActiveRecord::Type::Integer.new(:precision => precision, :limit => limit)
      end
    else
      ActiveRecord::Type::Decimal.new(:precision => precision, :scale => scale)
    end
  end

  register_class_with_limit m, %r(date)i,      ActiveRecord::Type::DateTime
  register_class_with_limit m, %r(raw)i,       RawType
  register_class_with_limit m, %r(timestamp)i, TimestampType

  m.register_type %r(xmltype)i, XmlType.new
end
insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = []) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 816
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
  # NOTE: ActiveRecord::Relation calls our {#next_sequence_value}
  # (from its `insert`) and passes the returned id_value here ...
  sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
  if id_value
    exec_update(sql, name, binds)
    return id_value
  else
    value = exec_insert(sql, name, binds, pk, sequence_name)
    id_value || last_inserted_id(value)
  end
end
jdbc_column_class() click to toggle source

@see ActiveRecord::ConnectionAdapters::JdbcAdapter#jdbc_column_class

# File lib/arjdbc/oracle/adapter.rb, line 58
def jdbc_column_class; ::ActiveRecord::ConnectionAdapters::OracleColumn end
modify_types(types) click to toggle source
Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 220
def modify_types(types)
  super(types)
  NATIVE_DATABASE_TYPES.each do |key, value|
    types[key] = value.dup
  end
  types
end
native_database_types() click to toggle source
Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 216
def native_database_types
  super.merge(NATIVE_DATABASE_TYPES)
end
next_sequence_value(sequence_name) click to toggle source

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 lib/arjdbc/oracle/adapter.rb, line 731
def next_sequence_value(sequence_name)
  # if sequence_name is set to :autogenerated then it means that primary key will be populated by trigger
  return nil if sequence_name == AUTOGENERATED_SEQUENCE_NAME
  sequence_name = quote_table_name(sequence_name)
  sql = "SELECT #{sequence_name}.NEXTVAL id FROM dual"
  log(sql, 'SQL') { @connection.next_sequence_value(sequence_name) }
end
prefetch_primary_key?(table_name = nil) click to toggle source

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

# File lib/arjdbc/oracle/adapter.rb, line 690
def prefetch_primary_key?(table_name = nil)
  return true if table_name.nil?
  do_not_prefetch_hash = @@do_not_prefetch_primary_key
  do_not_prefetch = do_not_prefetch_hash[ table_name = table_name.to_s ]
  if do_not_prefetch.nil?
    owner, desc_table_name, db_link = describe(table_name)
    do_not_prefetch_hash[table_name] = do_not_prefetch =
      ! has_primary_key?(table_name, owner, desc_table_name, db_link) ||
      has_primary_key_trigger?(table_name, owner, desc_table_name, db_link)
  end
  ! do_not_prefetch
end
primary_key(table_name) click to toggle source

Returns just a table's primary key

# File lib/arjdbc/oracle/adapter.rb, line 764
def primary_key(table_name)
  pk_and_sequence = pk_and_sequence_for(table_name)
  pk_and_sequence && pk_and_sequence.first
end
quote(value, column = nil) click to toggle source

@override

Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 584
def quote(value, column = nil)
  return value if sql_literal?(value)

  column_type = column && column.type
  if column_type == :text || column_type == :binary
    return 'NULL' if value.nil? || value == ''
    if update_lob_value?(value, column)
      if /(.*?)\([0-9]+\)/ =~ ( sql_type = column.sql_type )
        %Q{empty_#{ $1.downcase }()}
      else
        %Q{empty_#{ sql_type.respond_to?(:downcase) ? sql_type.downcase : 'blob' }()}
      end
    else
      "'#{quote_string(value.to_s)}'"
    end
  elsif column_type == :xml
    "XMLTYPE('#{quote_string(value)}')" # XMLTYPE ?
  elsif column_type == :raw
    quote_raw(value)
  else
    if column.respond_to?(:primary) && column.primary && column.klass != String
      return value.to_i.to_s
    end

    if column_type == :datetime || column_type == :time
      if value.acts_like?(:time)
        %Q{TO_DATE('#{get_time(value).strftime("%Y-%m-%d %H:%M:%S")}','YYYY-MM-DD HH24:MI:SS')}
      else
        value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
      end
    elsif ( like_date = value.acts_like?(:date) ) || column_type == :date
      if value.acts_like?(:time) # value.respond_to?(:strftime)
        %Q{DATE'#{get_time(value).strftime("%Y-%m-%d")}'}
      elsif like_date
        %Q{DATE'#{quoted_date(value)}'} # DATE 'YYYY-MM-DD'
      else
        value.blank? ? 'NULL' : %Q{DATE'#{value}'} # assume correctly formated DATE (string)
      end
    elsif ( like_time = value.acts_like?(:time) ) || column_type == :timestamp
      if like_time
        %Q{TIMESTAMP'#{quoted_date(value, true)}'} # TIMESTAMP 'YYYY-MM-DD HH24:MI:SS.FF'
      else
        value.blank? ? 'NULL' : %Q{TIMESTAMP'#{value}'} # assume correctly formated TIMESTAMP (string)
      end
    else
      super
    end
  end
end
quote_column_name(name, handle_expression = false) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 561
def quote_column_name(name, handle_expression = false)
  # if only valid lowercase column characters in name
  if ( name = name.to_s ) =~ LOWER_CASE_ONLY
    # putting double-quotes around an identifier causes Oracle to treat the
    # identifier as case sensitive (otherwise assumes case-insensitivity) !
    # all upper case is an exception, where double-quotes are meaningless
    "\"#{name.upcase}\"" # name.upcase
  else
    if handle_expression
      name =~ /^[a-z][a-z_0-9\$#\-]*$/i ? "\"#{name}\"" : name
    else
      # remove double quotes which cannot be used inside quoted identifier
      "\"#{name.gsub('"', '')}\""
    end
  end
end
quote_raw(value) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 646
def quote_raw(value)
  value = value.unpack('C*') if value.is_a?(String)
  "'#{value.map { |x| "%02X" % x }.join}'"
end
quote_table_name(name) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 553
def quote_table_name(name)
  name.to_s.split('.').map{ |n| n.split('@').map{ |m| quote_column_name(m) }.join('@') }.join('.')
end
quoted_date(value, time = nil) click to toggle source

Quote date/time values for use in SQL input. Includes milliseconds if the value is a Time responding to usec. @override

# File lib/arjdbc/oracle/adapter.rb, line 637
def quoted_date(value, time = nil)
  if time || ( time.nil? && value.acts_like?(:time) )
    usec = value.respond_to?(:usec) && (value.usec / 10000.0).round # .428000 -> .43
    return "#{get_time(value).to_s(:db)}.#{sprintf("%02d", usec)}" if usec
    # value.strftime("%Y-%m-%d %H:%M:%S")
  end
  value.to_s(:db)
end
release_savepoint(name = nil) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 349
def release_savepoint(name = nil)
  # no RELEASE SAVEPOINT statement in Oracle (JDBC driver throws "Unsupported feature")
end
remove_column(table_name, column_name, type = nil, options = {}) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 441
def remove_column(table_name, column_name, type = nil, options = {})
  do_remove_column(table_name, column_name)
end
Also aliased as: remove_columns
remove_columns(table_name, column_name, type = nil, options = {})
Alias for: remove_column
remove_index(table_name, options = {}) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 389
def remove_index(table_name, options = {})
  index_name = index_name(table_name, options)
  unless index_name_exists?(table_name, index_name, true)
    # sometimes options can be String or Array with column names
    options = {} unless options.is_a?(Hash)
    if options.has_key? :name
      options_without_column = options.dup
      options_without_column.delete :column
      index_name_without_column = index_name(table_name, options_without_column)
      return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
    end
    raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
  end
  execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
  execute "DROP INDEX #{quote_column_name(index_name)}"
end
rename_column(table_name, column_name, new_column_name) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 433
def rename_column(table_name, column_name, new_column_name)
  execute "ALTER TABLE #{quote_table_name(table_name)} " <<
    "RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
end
rename_table(name, new_name) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 267
def rename_table(name, new_name)
  if new_name.to_s.length > table_name_length
    raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
  end
  if "#{new_name}_seq".to_s.length > sequence_name_length
    raise ArgumentError, "New sequence name '#{new_name}_seq' is too long; the limit is #{sequence_name_length} characters"
  end
  execute "RENAME #{quote_table_name(name)} TO #{quote_table_name(new_name)}"
  execute "RENAME #{quote_table_name("#{name}_seq")} TO #{quote_table_name("#{new_name}_seq")}" rescue nil
end
select(sql, name = nil, binds = []) click to toggle source
Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 677
def select(sql, name = nil, binds = [])
  result = super # AR::Result (4.0) or Array (<= 3.2)
  result.columns.delete('raw_rnum_') if result.respond_to?(:columns)
  result.each { |row| row.delete('raw_rnum_') } # Hash rows even for AR::Result
  result
end
sequence_name_length() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 241
def sequence_name_length; IDENTIFIER_LENGTH end
sql_for_insert(sql, pk, id_value, sequence_name, binds) click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 806
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
  unless id_value || pk.nil?
    if pk && use_insert_returning?
      sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO ?"
    end
  end
  [ sql, binds ]
end
supports_explain?() click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 661
def supports_explain?; true end
supports_foreign_keys?() click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 770
def supports_foreign_keys?; true end
supports_migrations?() click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 652
def supports_migrations?; true end
supports_primary_key?() click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 655
def supports_primary_key?; true end
supports_savepoints?() click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 658
def supports_savepoints?; true end
supports_views?() click to toggle source

@override

# File lib/arjdbc/oracle/adapter.rb, line 664
def supports_views?; true end
table_alias_length() click to toggle source

maximum length of Oracle identifiers is 30

# File lib/arjdbc/oracle/adapter.rb, line 237
def table_alias_length; IDENTIFIER_LENGTH; end
table_definition(*args) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 118
def table_definition(*args)
  new_table_definition(TableDefinition, *args)
end
table_name_length() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 238
def table_name_length;  IDENTIFIER_LENGTH; end
tables() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 521
def tables
  @connection.tables(nil, oracle_schema)
end
tablespace(table_name) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 530
def tablespace(table_name)
  select_value "SELECT tablespace_name FROM user_tables WHERE table_name='#{table_name.to_s.upcase}'"
end
temporary_table?(table_name) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 517
def temporary_table?(table_name)
  select_value("SELECT temporary FROM user_tables WHERE table_name = '#{table_name.upcase}'") == 'Y'
end
truncate(table_name, name = nil) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 666
def truncate(table_name, name = nil)
  execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
end
type_to_sql(type, limit = nil, precision = nil, scale = nil) click to toggle source

@override

Calls superclass method
# File lib/arjdbc/oracle/adapter.rb, line 289
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  case type.to_sym
  when :binary
    # { BLOB | BINARY LARGE OBJECT } [ ( length [{K |M |G }] ) ]
    # although Oracle does not like limit (length) with BLOB (or CLOB) :
    #
    # CREATE TABLE binaries (data BLOB, short_data BLOB(1024));
    # ORA-00907: missing right parenthesis             *
    #
    # TODO do we need to worry about NORMAL vs. non IN-TABLE BLOBs ?!
    # http://dba.stackexchange.com/questions/8770/improve-blob-writing-performance-in-oracle-11g
    # - if the LOB is smaller than 3900 bytes it can be stored inside the
    #   table row; by default this is enabled,
    #   unless you specify DISABLE STORAGE IN ROW
    # - normal LOB - stored in a separate segment, outside of table,
    #   you may even put it in another tablespace;
    super(type, nil, nil, nil)
  when :text
    super(type, nil, nil, nil)
  else
    super
  end
end
unquote_table_name(name) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 578
def unquote_table_name(name)
  name = name[1...-1] if name[0, 1] == '"'
  name.upcase == name ? name.downcase : name
end
update_lob_value?(value, column = nil) click to toggle source

@see update_lob_values? @see ArJdbc::Util::SerializedAttributes#update_lob_columns

# File lib/arjdbc/oracle/adapter.rb, line 75
def update_lob_value?(value, column = nil)
  Oracle.update_lob_values? && ! prepared_statements? && ! ( value.nil? || value == '' )
end
use_insert_returning?() click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 860
def use_insert_returning?
  if @use_insert_returning.nil?
    @use_insert_returning = false
  end
  @use_insert_returning
end

Protected Instance Methods

insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) click to toggle source

@override (for AR <= 3.0)

# File lib/arjdbc/oracle/adapter.rb, line 789
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
  # if PK is already pre-fetched from sequence or if there is no PK :
  if id_value || pk.nil?
    execute(sql, name)
    return id_value
  end

  if pk && use_insert_returning? # true by default on AR <= 3.0
    sql = "#{sql} RETURNING #{quote_column_name(pk)} INTO ?"
    exec_insert_returning(sql, name, nil, pk)
  else
    execute(sql, name)
  end
end

Private Instance Methods

_execute(sql, name = nil) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 869
def _execute(sql, name = nil)
  if self.class.select?(sql)
    @connection.execute_query_raw(sql)
  elsif self.class.insert?(sql)
    @connection.execute_insert(sql)
  else
    @connection.execute_update(sql)
  end
end
describe(table_name, owner = schema_owner(false)) click to toggle source

do not force reading #schema_owner as we're read on our own …

# File lib/arjdbc/oracle/adapter.rb, line 913
def describe(table_name, owner = schema_owner(false))
  @connection.describe(table_name, owner)
end
do_remove_column(table_name, column_name) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 457
def do_remove_column(table_name, column_name)
  execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
end
extract_order_columns(order_by) { |columns| ... } click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 509
def extract_order_columns(order_by)
  columns = order_by.split(',')
  columns.map!(&:strip); columns.reject!(&:blank?)
  columns = yield(columns) if block_given?
  columns.zip( (0...columns.size).to_a )
end
extract_table_ref_from_insert_sql(sql) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 879
def extract_table_ref_from_insert_sql(sql)
  table = sql.split(" ", 4)[2]
  if idx = table.index('(')
    table = table[0...idx] # INTO table(col1, col2) ...
  end
  unquote_table_name(table)
end
next_id_value(sql, sequence_name = nil) click to toggle source

private :exec_insert_returning

# File lib/arjdbc/oracle/adapter.rb, line 849
def next_id_value(sql, sequence_name = nil)
  # Assume the SQL contains a bind-variable for the ID
  sequence_name ||= begin
    # Extract the table from the insert SQL. Yuck.
    table = extract_table_ref_from_insert_sql(sql)
    default_sequence_name(table)
  end
  next_sequence_value(sequence_name)
end
oracle_downcase(column_name) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 917
def oracle_downcase(column_name)
  return nil if column_name.nil?
  column_name =~ /[a-z]/ ? column_name : column_name.downcase
end
oracle_schema() click to toggle source

In Oracle, schemas are usually created under your username : www.oracle.com/technology/obe/2day_dba/schema/schema.htm

A schema is the set of objects (tables, views, indexes, etc) that belongs to an user, often used as another way to refer to an Oracle user account.

But allow separate configuration as “schema:” anyway (see #53)

# File lib/arjdbc/oracle/adapter.rb, line 894
def oracle_schema
  if @config[:schema]
    @config[:schema].to_s
  elsif @config[:username]
    @config[:username].to_s
  end
end
pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil, db_link = nil) click to toggle source
# File lib/arjdbc/oracle/adapter.rb, line 739
def pk_and_sequence_for(table_name, owner = nil, desc_table_name = nil, db_link = nil)
  (owner, desc_table_name, db_link) = describe(table_name) unless desc_table_name

  seqs = "SELECT us.sequence_name" <<
    " FROM all_sequences#{db_link} us" <<
    " WHERE us.sequence_owner = '#{owner}'" <<
      " AND us.sequence_name = '#{desc_table_name}_SEQ'"
  seqs = select_values(seqs, 'Sequence')

  # changed back from user_constraints to all_constraints for consistency
  pks = "SELECT cc.column_name" <<
    " FROM all_constraints#{db_link} c, all_cons_columns#{db_link} cc" <<
    " WHERE c.owner = '#{owner}'" <<
      " AND c.table_name = '#{desc_table_name}'" <<
      " AND c.constraint_type = 'P'" <<
      " AND cc.owner = c.owner" <<
      " AND cc.constraint_name = c.constraint_name"
  pks = select_values(pks, 'Primary Key')

  # only support single column keys
  pks.size == 1 ? [oracle_downcase(pks.first), oracle_downcase(seqs.first)] : nil
end
schema_owner(force = true) click to toggle source

default schema owner

# File lib/arjdbc/oracle/adapter.rb, line 903
def schema_owner(force = true)
  unless defined? @schema_owner
    username = config[:username] ? config[:username].to_s : nil
    username = jdbc_connection.meta_data.user_name if force && username.nil?
    @schema_owner = username.nil? ? nil : username.upcase
  end
  @schema_owner
end