module ActiveRecord
  module ConnectionAdapters
    # interface independent methods
    class OracleEnhancedConnection #:nodoc:
      def self.create(config)
        case ORACLE_ENHANCED_CONNECTION
        when :oci
          OracleEnhancedOCIConnection.new(config)
        when :jdbc
          OracleEnhancedJDBCConnection.new(config)
        else
          nil
        end
      end

      attr_reader :raw_connection

      # 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)
        return nil if column_name.nil?
        column_name =~ /[a-z]/ ? column_name : column_name.downcase
      end

      # Used always by JDBC connection as well by OCI connection when describing tables over database link
      def describe(name)
        name = name.to_s
        if name.include?('@')
          name, db_link = name.split('@')
          default_owner = select_value("SELECT username FROM all_db_links WHERE db_link = '#{db_link.upcase}'")
          db_link = "@#{db_link}"
        else
          db_link = nil
          default_owner = @owner
        end
        real_name = ActiveRecord::ConnectionAdapters::OracleEnhanced::Quoting.valid_table_name?(name) ? name.upcase : name
        if real_name.include?('.')
          table_owner, table_name = real_name.split('.')
        else
          table_owner, table_name = default_owner, real_name
        end
        sql = <<-SQL
          SELECT owner, table_name, 'TABLE' name_type
          FROM all_tables#{db_link}
          WHERE owner = '#{table_owner}'
            AND table_name = '#{table_name}'
          UNION ALL
          SELECT owner, view_name table_name, 'VIEW' name_type
          FROM all_views#{db_link}
          WHERE owner = '#{table_owner}'
            AND view_name = '#{table_name}'
          UNION ALL
          SELECT table_owner, DECODE(db_link, NULL, table_name, table_name||'@'||db_link), 'SYNONYM' name_type
          FROM all_synonyms#{db_link}
          WHERE owner = '#{table_owner}'
            AND synonym_name = '#{table_name}'
          UNION ALL
          SELECT table_owner, DECODE(db_link, NULL, table_name, table_name||'@'||db_link), 'SYNONYM' name_type
          FROM all_synonyms#{db_link}
          WHERE owner = 'PUBLIC'
            AND synonym_name = '#{real_name}'
        SQL
        if result = select_one(sql)
          case result['name_type']
          when 'SYNONYM'
            describe("#{result['owner'] && "#{result['owner']}."}#{result['table_name']}#{db_link}")
          else
            db_link ? [result['owner'], result['table_name'], db_link] : [result['owner'], result['table_name']]
          end
        else
          raise OracleEnhancedConnectionException, %Q{"DESC #{name}" failed; does it exist?}
        end
      end

      # Returns a record hash with the column names as keys and column values
      # as values.
      def select_one(sql)
        result = select(sql)
        result.first if result
      end

      # Returns a single value from a record
      def select_value(sql)
        if result = select_one(sql)
          result.values.first
        end
      end

      # Returns an array of the values of the first column in a select:
      #   select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
      def select_values(sql, name = nil)
        result = select(sql, name = nil)
        result.map { |r| r.values.first }
      end
    end

    # Returns array with major and minor version of database (e.g. [12, 1])
    def database_version
      raise NoMethodError, "Not implemented for this raw driver"
    end

    class OracleEnhancedConnectionException < StandardError #:nodoc:
    end
  end
end

# if MRI or YARV
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
  ORACLE_ENHANCED_CONNECTION = :oci
  require 'active_record/connection_adapters/oracle_enhanced/oci_connection'
# if JRuby
elsif RUBY_ENGINE == 'jruby'
  ORACLE_ENHANCED_CONNECTION = :jdbc
  require 'active_record/connection_adapters/oracle_enhanced/jdbc_connection'
else
  raise "Unsupported Ruby engine #{RUBY_ENGINE}"
end