20-db-bootstrap-procs.tcl

Does not contain a contract.

Location:
/packages/acs-bootstrap-installer/tcl/20-db-bootstrap-procs.tcl

Related Files

[ hide source ] | [ make this the default ]

File Contents

ad_proc -public db_available_pools {dbn} {
    Returns a list of the available pools for the given database name.

    <p>
    We define this here in 20-db-bootstrap-procs.tcl rather than
    acs-tcl/tcl/00-database-procs.tcl, as we also need to call it from
    db_bootstrap_set_db_type, below, and from db_bootstrap_checks,
    before all the rest of the db_* API code in 00-database-procs.tcl
    is sourced.

    @param dbn The database name to use.  If empty_string, uses the default database.

    @author Andrew Piskorski (atp@piskorski.com)
    @creation-date 2003/03/16
} {
    if { $dbn eq "" } {
        set dbn $::acs::default_database
    }
    # When updating from versions before 5.10.0d4, namespace variable
    # might not be initialized. Get values from conf in this case.
    if {![info exists ::acs::db_pools($dbn)]} {
        db_bootstrap_set_db_type errors
    }
    return $::acs::db_pools($dbn)
}

ad_proc -private db_pool_to_dbn_init {} {
    Simply initializes the <code>db_pool_to_dbn</code> nsv, which is
    used by "<code>db_driverkey -handle</code>".

    @author Andrew Piskorski (atp@piskorski.com)
    @creation-date 2003/04/09

    @see db_driverkey
} {
    foreach dbn [array names ::acs::db_pools] {
        foreach pool [db_available_pools $dbn] {
            nsv_set db_pool_to_dbn $pool $dbn
            set ::acs::db_pool_to_dbn($pool$dbn
        }
    }
}


ad_proc -private db_bootstrap_set_db_type { errors } {

    @author Don Baccus (dhogaza@pacifier.com)
    @param errors Name of variable in caller's space that should receive
    error messages.

    Determine which database engine the user's configured, and do
    db-independent errors checking.

    The "errors" param hack should change eventually, I don't have
    time at the moment to change how the bootstrapper communicates
    database problems to the installer.
} {
    set proc_name {Database API}

    # Might as well get el grosso hacko out of the way...
    upvar $errors database_problem

    # DRB: I've reorganized this a bit from the standard ACS 4.1.  In their
    # version, they'd allocate a handle from the server default pool, check
    # for a recent Oracle driver and assume everything was A-OK if the check
    # succeeded.
    #
    # There are some problems with this approach:
    #
    # 1. The "AvailablePool" parameter specifies the pools to be used by the ACS.
    #    The default pool needn't be listed as an available pool, therefore, in a
    #    mixed db environment the check strategy described above might not actually
    #    be checking any pool designated for ACS use.
    #
    #    In fact, if it weren't for the bootstrap check code there would be no
    #    need at all to configure a default database pool!
    #
    #    The standard ACS check was fine as far as it went in the normal case
    #    where no AvailablePool parameters exist, as in that case the ACS
    #    slurps up all pools.  I expect mixed db environments to be more common
    #    within the OpenACS community, though, so we should do a better job of
    #    checking.  This will especially be true of users migrating from an
    #    [Open]ACS 3.x site or ACS 4.x classic site.
    #
    # 2. There was no checking to make sure that *all* pools are correctly
    #    configured.  Even in an Oracle-only environment one could easy mistype a
    #    username or the like for one of the pools set aside for ACS use, and
    #    this would not be cleanly caught and reported.
    #
    # 3. There was no checking to make sure that *all* pools are of the same RDBMS
    #    type.  This is important in a mixed-db environment.
    #
    # The strategy I've adopted is to initialize the list of available pools in the
    # bootstrap code, then check that each pool can be allocated, that each is of
    # a recognized database type (oracle and postgres as of 3/2001), and that each
    # pool is of the same database type.  We could also make certain that we're
    # connecting to the same database and user in each pool, but at the moment
    # that's seems anal even by DRB's standards.

    # The same information is as well in 0-acs-init.tcl; it is kept
    # here for a while to guarantee a smooth migration, since the
    # db-interface is essential and we have to deal with situations,
    # where still an old 0-acs-init.tcl is active. This could be
    # removed around OpenACS 6.*
    #
    set ::acs::known_database_types {
        {oracle Oracle Oracle}
        {postgresql PostgreSQL PostgreSQL}
    }


    #
    # Initialize the list of available pools
    #

    set server_name [ns_info server]
    set config_path "ns/server/$server_name/acs/database"
    set all_pools [ns_db pools]

    # unset namespaced array of database pools
    unset -nocomplain ::acs::db_pools

    set database_names [ns_config $config_path {database_names}]

    if { [llength $database_names] <= 0 } {
        # Fall back to old OpenACS 4.6.x pre-multi-db style.
        set old_availablepool_p 1
        set default_dbn {default}
    } else {
        # The config file is using the new multi-db format.
        set old_availablepool_p 0

        set default_dbn [lindex $database_names 0]
        if { $default_dbn eq "" } {
            set default_dbn {default}
            set old_availablepool_p 1

        } else {
            foreach dbn $database_names {
                # TODO: For each pool, may want to add a check against
                # all_pools to ensure that the pool is valid.

                set dbn_pools [ns_config $config_path "pools_${dbn}"]
                set ::acs::db_pools($dbn$dbn_pools
                ns_log Notice "$proc_name: For database '$dbn', the following pools are available: $dbn_pools"
            }

            if { [db_available_pools $default_dbn] eq "" } {
                ns_log Error "$proc_name: No pools specified for database '$default_dbn'."
                set old_availablepool_p 1
            }
        }
    }

    set ::acs::default_database $default_dbn

    ns_log Notice "$proc_name: Default database (dbn) is: '$default_dbn'"

    if { $old_availablepool_p } {
        # We ONLY do this as a fallback, if something was wrong with
        # the newer multi-db config above, or if it was simply missing
        # entirely:  --atp@piskorski.com, 2003/03/17 00:55 EST

        set dbn_pools [list]
        set the_set [ns_configsection $config_path]
        if { $the_set ne "" } {
            foreach {key value} [ns_set array $the_set] {
                if { [string tolower $key] == "availablepool" } {
                    lappend dbn_pools $value
                }
            }
        }
        set ::acs::db_pools($default_dbn$dbn_pools
    }
    set ::acs::db_pools(""$::acs::db_pools($::acs::default_database)

    set pools [db_available_pools {}]
    if { [llength $pools] <= 0 } {
        set ::acs::db_pools($default_dbn$all_pools
        set ::acs::db_pools(""$all_pools
        set pools $all_pools
        ns_log Notice "$proc_name: Using ALL database pools for OpenACS."
    }
    db_pool_to_dbn_init
    ns_log Notice "$proc_name: The following pools are available for OpenACS: $pools"

    # DRB: if the user hasn't given us enough database pools might as well tell
    # them in plain english

    if { [llength $pools] == 0 } {
        set database_problem "There are no database pools specified in your NaviServer configuration file."
    } elseif { [llength $pools] < 3 } {
        set database_problem "OpenACS requires three database pools in order to run correctly."
    }

    # We're done with the multi-db dbn stuff, from now on we deal only
    # with the OpenACS default database pools:
    #
    # TODO: For now the below pool-checking code runs ONLY for the
    # default database.  Should probably extend the checking to all
    # configured databases:
    #
    # --atp@piskorski.com, 2003/03/17 00:53 EST

    # DRB: Try to allocate a handle from each pool and determine its database type.
    # I wrote this to break after the first allocation failure because a defunct
    # oracle process is created if Oracle's not running at all, causing AOLserver
    # to hang on the second attempt to allocate a handle.   At least on my RH 6.2
    # Linux box, it does.

    nsv_set ad_database_type . ""
    nsv_set ad_database_version . ""

    set bad_pools [list]
    set long_error 0
    foreach pool $pools {
        if { [catch { set db [ns_db gethandle -timeout 15 $pool]} errorMsg] || $db eq "" } {
            ns_log Warning "$proc_name: couldn't allocate a handle from database pool \"$pool\"."
            lappend bad_pools "<li>OpenACS could not allocate a handle from database pool \"$pool\"."
            set long_error 1
            break
        } else {
            set this_suffix ""
            if { [catch { set driver [ns_db dbtype $db] } errmsg] } {
                set database_problem "RDBMS type could not be determined: $errmsg"
                ns_log Error "$proc_name: RDBMS type could not be determined: $errmsg"
            } else {
                foreach known_database_type $::acs::known_database_types {

                    set this_type [lindex $known_database_type 1]

                    # we do a string match here, because we want to
                    # match against Oracle, Oracle8, Oracle10, etc..
                    if { [string match ${this_type}$driver] } {
                        set this_suffix [lindex $known_database_type 0]
                        break
                    }
                }
            }
            #ns_log notice "pool $pool: got handle $db suffix $this_suffix"

            ns_db releasehandle $db
            if { $this_suffix eq "" } {
                ns_log Notice "$proc_name: couldn't determine RDBMS type of database pool \"$pool\"."
                lappend bad_pools "<li>OpenACS could not determine the RDBMS type associated with pool \"$pool\"."
                set long_error 1
            } elseif { [nsv_get ad_database_type .] eq "" } {
                nsv_set ad_database_type . $this_suffix
                #
                # For the time being, keep the info in the nsv for
                # backwards compatibility and a version in a
                # per-thead (namespaced) variable
                #
                set ::acs::database_type $this_suffix

            } elseif { ![string match $this_suffix [nsv_get ad_database_type .]] } {
                ns_log Notice "$proc_name: Database pool \"$pool\" type \"$this_suffix\" differs from \"[nsv_get ad_database_type .]\"."
                lappend bad_pools "<li>Database pool \"$pool\" is of type \"$this_suffix\".  The
    first database pool available to OpenACS was of type \"[nsv_get ad_database_type .]\".  All database
    pools must be configured to use the same RDMBS engine, user and database."
            }
        }
    }

    if { [nsv_get ad_database_type .] eq "" } {
        set database_problem "RDBMS type could not be determined for any pool."
        ns_log Error "$proc_name: RDBMS type could not be determined for any pool."
    }

    if { [llength $bad_pools] > 0 } {
        set database_problem "<p>The following database pools generated errors:
    <ul>[join $bad_pools "\n"]</ul><p>\n"
        if { $long_error } {
            append database_problem "Possible causes might include:<p>
    <ul>
    <li>The database is not running.
    <li>The database driver has not been correctly installed.
    <li>The datasource or database user/password are incorrect.
    <li>You didn't define any database pools.
    </ul><p>"
        }
    }
}

# Local variables:
#    mode: tcl
#    tcl-indent-level: 4
#    indent-tabs-mode: nil
# End: