module DBI

++

Dispatch classes (Handle, DriverHandle, DatabaseHandle and StatementHandle)

$Id: utils.rb,v 1.5 2006/01/29 06:14:19 djberg96 Exp $

Constants

DEFAULT_TRACE_MODE

Module functions (of DBI)

DEFAULT_TRACE_OUTPUT
SQL_BIGINT
SQL_BINARY
SQL_BIT
SQL_BLOB
TODO
Find types for these (XOPEN?)

SQL_ARRAY =

SQL_BOOLEAN
SQL_CHAR

SQL type constants

SQL_CLOB
SQL_DATE
SQL_DECIMAL
SQL_DOUBLE
SQL_FETCH_ABSOLUTE
SQL_FETCH_FIRST
SQL_FETCH_LAST
SQL_FETCH_NEXT

Constants for fetch_scroll

SQL_FETCH_PRIOR
SQL_FETCH_RELATIVE
SQL_FLOAT
SQL_INTEGER
SQL_LONGVARBINARY
SQL_LONGVARCHAR
SQL_NUMERIC
SQL_OTHER

SQL_DISTINCT = SQL_OBJECT = SQL_NULL =

SQL_REAL
SQL_SMALLINT
SQL_TIME
SQL_TIMESTAMP
SQL_TINYINT
SQL_TYPE_NAMES

SQL_REF = SQL_STRUCT =

SQL_VARBINARY
SQL_VARCHAR
VERSION

Public Class Methods

available_drivers() click to toggle source

Returns a list (of String) of the currently available drivers on your system in 'dbi:driver:' format.

This currently does not work for rubygems installations, please see ::collect_drivers for reasons.

# File lib/dbi.rb, line 203
def available_drivers
    drivers = []
    collect_drivers.each do |key, value|
        drivers.push("dbi:#{key}:")
    end 
    return drivers
end
collect_drivers() click to toggle source

Return a list (of String) of the available drivers.

NOTE

This is non-functional for gem installations, due to the nature of how it currently works. A better solution for this will be provided in DBI 0.6.0.

# File lib/dbi.rb, line 184
def collect_drivers
    drivers = { }
    # FIXME rewrite this to leverage require and be more intelligent
    path = File.join(File.dirname(__FILE__), "dbd", "*.rb")
    Dir[path].each do |f|
        if File.file?(f)
            driver = File.basename(f, ".rb")
            drivers[driver] = f
        end
    end

    return drivers
end
connect(driver_url, user=nil, auth=nil, params=nil, &p) click to toggle source

Establish a database connection.

Format goes as such: “dbi:Driver:database_conn_args”

  • “dbi” is the literal string “dbi”. Case is unimportant.

  • “Driver” is the case-dependent name of your database driver class. The file “dbd/#{Driver}” will be required. If you are using rubygems to control your DBDs and DBI, you must make the gem's file path available via the “gem” command before this will work.

  • database_conn_args can be:

    • The database name.

    • A more complex key/value association (to indicate host, etc). This is driver dependent; you should consult your DBD documentation.

# File lib/dbi.rb, line 144
def connect(driver_url, user=nil, auth=nil, params=nil, &p)
    dr, db_args = _get_full_driver(driver_url)
    dh = dr[0] # driver-handle
    dh.convert_types = @@convert_types
    @@last_connection = dh.connect(db_args, user, auth, params, &p)
end
convert_types() click to toggle source

Return the current status of type conversion at this level. This status will be propogated to any new DatabaseHandles created.

# File lib/dbi.rb, line 119
def self.convert_types
    @@convert_types
end
convert_types=(bool) click to toggle source

Set the current status of type conversion at this level. This status will be propogated to any new DatabaseHandles created.

# File lib/dbi.rb, line 125
def self.convert_types=(bool)
    @@convert_types = bool
end
data_sources(driver) click to toggle source

Attempt to collect the available data sources to the driver, specified in ::connect format.

The result is heavily dependent on the driver's ability to enumerate these sources, and results will vary.

# File lib/dbi.rb, line 216
def data_sources(driver)
    db_driver, = parse_url(driver)
    db_driver = load_driver(db_driver)
    dh = @@driver_map[db_driver][0]
    dh.data_sources
end
disconnect_all( driver = nil ) click to toggle source

Attempt to disconnect all database handles. If a driver is provided, disconnections will happen under that scope. Otherwise, all loaded drivers (and their handles) will be attempted.

# File lib/dbi.rb, line 228
def disconnect_all( driver = nil )
    if driver.nil?
        @@driver_map.each {|k,v| v[0].disconnect_all}
    else
        db_driver, = parse_url(driver)
        @@driver_map[db_driver][0].disconnect_all
    end
end
last_connection() click to toggle source

Return the last connection attempted.

# File lib/dbi.rb, line 113
def self.last_connection
    @@last_connection
end
trace(mode=nil, output=nil) click to toggle source

Enable tracing mode. Requires that 'dbi/trace' be required before it does anything.

As of 0.4.0, this mode does not do anything either way, so this currently just throws an InterfaceError. This issue is expected to be resolved in the next release.

# File lib/dbi.rb, line 171
def trace(mode=nil, output=nil)
    # FIXME trace
    raise InterfaceError, "the trace module has been removed until it actually works."
    @@trace_mode   = mode   || @@trace_mode   || DBI::DEFAULT_TRACE_MODE
    @@trace_output = output || @@trace_output || DBI::DEFAULT_TRACE_OUTPUT
end

Private Class Methods

load_driver(driver_name) click to toggle source

Given a driver name, locate and load the associated DBD package, generate a DriverHandle and return it.

# File lib/dbi.rb, line 241
def load_driver(driver_name)
    @@driver_monitor.synchronize do
        unless @@driver_map[driver_name]
            dc = driver_name.downcase

            # caseless look for drivers already loaded
            found = @@driver_map.keys.find {|key| key.downcase == dc}
            return found if found

            begin
                require "dbd/#{driver_name}"
            rescue LoadError => e1
                # see if you can find it in the path
                unless @@caseless_driver_name_map
                    @@caseless_driver_name_map = { } 
                    collect_drivers.each do |key, value|
                        @@caseless_driver_name_map[key.downcase] = value
                    end
                end

                begin
                    require @@caseless_driver_name_map[dc] if @@caseless_driver_name_map[dc]
                rescue LoadError => e2
                    raise e1.class, "Could not find driver #{driver_name} or #{driver_name.downcase} (error: #{e1.message})"
                end
            end

            # On a filesystem that is not case-sensitive (e.g., HFS+ on Mac OS X),
            # the initial require attempt that loads the driver may succeed even
            # though the lettercase of driver_name doesn't match the actual
            # filename. If that happens, const_get will fail and it become
            # necessary to look though the list of constants and look for a
            # caseless match.  The result of this match provides the constant
            # with the proper lettercase -- which can be used to generate the
            # driver handle.

            dr = nil
            dr_error = nil
            begin
                dr = DBI::DBD.const_get(driver_name.intern)
            rescue NameError => dr_error
                # caseless look for constants to find actual constant
                dc = driver_name.downcase
                found = DBI::DBD.constants.find { |e| e.downcase == dc }
                dr = DBI::DBD.const_get(found.intern) unless found.nil?
            end

            # If dr is nil at this point, it means the underlying driver
            # failed to load.  This usually means it's not installed, but
            # can fail for other reasons.
            if dr.nil?
                err = "Unable to load driver '#{driver_name}'"

                if dr_error
                    err += " (underlying error: #{dr_error.message})"
                else
                    err += " (BUG: could not determine underlying error)"
                end

                raise DBI::InterfaceError, err
            end

            dbd_dr = dr::Driver.new
            drh = DBI::DriverHandle.new(dbd_dr, @@convert_types)
            drh.driver_name = dr.driver_name
            # FIXME trace
            # drh.trace(@@trace_mode, @@trace_output)
            @@driver_map[driver_name] = [drh, dbd_dr]
            return driver_name 
        else
            return driver_name
        end
    end
rescue LoadError, NameError
    if $SAFE >= 1
        raise InterfaceError, "Could not load driver (#{$!.message}). Note that in SAFE mode >= 1, driver URLs have to be case sensitive!"
    else
        raise InterfaceError, "Could not load driver (#{$!.message})"
    end
end
parse_url(driver_url) click to toggle source

Splits a DBI URL into two components - the database driver name and the datasource (along with any options, if any) and returns a two element array, e.g. 'dbi:foo:bar' would return ['foo','bar'].

A regular expression is used instead of a simple split to validate the proper format for the URL. If it isn't correct, an Interface error is raised.

# File lib/dbi.rb, line 329
def parse_url(driver_url)
    if driver_url =~ /^(DBI|dbi):([^:]+)(:(.*))$/ 
        [$2, $4]
    else
        raise InterfaceError, "Invalid Data Source Name"
    end
end