svncommit
2005-10-26 d13c36941c585e84c8986e1eb47a296646cc7a21

missing mdb2 files


7 files added
3845 ■■■■■ changed files
program/lib/MDB2/Date.php 180 ●●●●● patch | view | raw | blame | history
program/lib/MDB2/Driver/Datatype/Common.php 1263 ●●●●● patch | view | raw | blame | history
program/lib/MDB2/Driver/Manager/Common.php 616 ●●●●● patch | view | raw | blame | history
program/lib/MDB2/Extended.php 605 ●●●●● patch | view | raw | blame | history
program/lib/MDB2/Iterator.php 285 ●●●●● patch | view | raw | blame | history
program/lib/MDB2/LOB.php 146 ●●●●● patch | view | raw | blame | history
program/lib/MDB2/Wrapper/peardb.php 750 ●●●●● patch | view | raw | blame | history
program/lib/MDB2/Date.php
New file
@@ -0,0 +1,180 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5                                                 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
// | Stig. S. Bakken, Lukas Smith                                         |
// | All rights reserved.                                                 |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
// | API as well as database abstraction for PHP applications.            |
// | This LICENSE is in the BSD license style.                            |
// |                                                                      |
// | Redistribution and use in source and binary forms, with or without   |
// | modification, are permitted provided that the following conditions   |
// | are met:                                                             |
// |                                                                      |
// | Redistributions of source code must retain the above copyright       |
// | notice, this list of conditions and the following disclaimer.        |
// |                                                                      |
// | Redistributions in binary form must reproduce the above copyright    |
// | notice, this list of conditions and the following disclaimer in the  |
// | documentation and/or other materials provided with the distribution. |
// |                                                                      |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission.                                                  |
// |                                                                      |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
// | POSSIBILITY OF SUCH DAMAGE.                                          |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org>                           |
// +----------------------------------------------------------------------+
//
// $Id$
//
/**
 * @package  MDB2
 * @category Database
 * @author   Lukas Smith <smith@pooteeweet.org>
 */
/**
 * Several methods to convert the MDB2 native timestamp format (ISO based)
 * to and from data structures that are convienient to worth with in side of php.
 * For more complex date arithmetic please take a look at the Date package in PEAR
 *
 * @package MDB2
 * @category Database
 * @author  Lukas Smith <smith@pooteeweet.org>
 */
class MDB2_Date
{
    // {{{ mdbNow()
    /**
     * return the current datetime
     *
     * @return string current datetime in the MDB2 format
     * @access public
     */
    function mdbNow()
    {
        return date('Y-m-d H:i:s');
    }
    // }}}
    // {{{ mdbToday()
    /**
     * return the current date
     *
     * @return string current date in the MDB2 format
     * @access public
     */
    function mdbToday()
    {
        return date('Y-m-d');
    }
    // }}}
    // {{{ mdbTime()
    /**
     * return the current time
     *
     * @return string current time in the MDB2 format
     * @access public
     */
    function mdbTime()
    {
        return date('H:i:s');
    }
    // }}}
    // {{{ date2Mdbstamp()
    /**
     * convert a date into a MDB2 timestamp
     *
     * @param integer $hour hour of the date
     * @param integer $minute minute of the date
     * @param integer $second second of the date
     * @param integer $month month of the date
     * @param integer $day day of the date
     * @param integer $year year of the date
     * @return string a valid MDB2 timestamp
     * @access public
     */
    function date2Mdbstamp($hour = null, $minute = null, $second = null,
        $month = null, $day = null, $year = null)
    {
        return MDB2_Date::unix2Mdbstamp(mktime($hour, $minute, $second, $month, $day, $year, -1));
    }
    // }}}
    // {{{ unix2Mdbstamp()
    /**
     * convert a unix timestamp into a MDB2 timestamp
     *
     * @param integer $unix_timestamp a valid unix timestamp
     * @return string a valid MDB2 timestamp
     * @access public
     */
    function unix2Mdbstamp($unix_timestamp)
    {
        return date('Y-m-d H:i:s', $unix_timestamp);
    }
    // }}}
    // {{{ mdbstamp2Unix()
    /**
     * convert a MDB2 timestamp into a unix timestamp
     *
     * @param integer $mdb_timestamp a valid MDB2 timestamp
     * @return string unix timestamp with the time stored in the MDB2 format
     * @access public
     */
    function mdbstamp2Unix($mdb_timestamp)
    {
        $arr = MDB2_Date::mdbstamp2Date($mdb_timestamp);
        return mktime($arr['hour'], $arr['minute'], $arr['second'], $arr['month'], $arr['day'], $arr['year'], -1);
        }
    // }}}
    // {{{ mdbstamp2Date()
    /**
     * convert a MDB2 timestamp into an array containing all
     * values necessary to pass to php's date() function
     *
     * @param integer $mdb_timestamp a valid MDB2 timestamp
     * @return array with the time split
     * @access public
     */
    function mdbstamp2Date($mdb_timestamp)
    {
        list($arr['year'], $arr['month'], $arr['day'], $arr['hour'], $arr['minute'], $arr['second']) =
            sscanf($mdb_timestamp, "%04u-%02u-%02u %02u:%02u:%02u");
        return $arr;
    }
    // }}}
}
?>
program/lib/MDB2/Driver/Datatype/Common.php
New file
@@ -0,0 +1,1263 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5                                                 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
// | Stig. S. Bakken, Lukas Smith                                         |
// | All rights reserved.                                                 |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
// | API as well as database abstraction for PHP applications.            |
// | This LICENSE is in the BSD license style.                            |
// |                                                                      |
// | Redistribution and use in source and binary forms, with or without   |
// | modification, are permitted provided that the following conditions   |
// | are met:                                                             |
// |                                                                      |
// | Redistributions of source code must retain the above copyright       |
// | notice, this list of conditions and the following disclaimer.        |
// |                                                                      |
// | Redistributions in binary form must reproduce the above copyright    |
// | notice, this list of conditions and the following disclaimer in the  |
// | documentation and/or other materials provided with the distribution. |
// |                                                                      |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission.                                                  |
// |                                                                      |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
// | POSSIBILITY OF SUCH DAMAGE.                                          |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org>                           |
// +----------------------------------------------------------------------+
//
// $Id$
require_once 'MDB2/LOB.php';
/**
 * @package  MDB2
 * @category Database
 * @author   Lukas Smith <smith@pooteeweet.org>
 */
/**
 * MDB2_Driver_Common: Base class that is extended by each MDB2 driver
 *
 * @package MDB2
 * @category Database
 * @author Lukas Smith <smith@pooteeweet.org>
 */
class MDB2_Driver_Datatype_Common extends MDB2_Module_Common
{
    var $valid_types = array(
        'text'      => true,
        'boolean'   => true,
        'integer'   => true,
        'decimal'   => true,
        'float'     => true,
        'date'      => true,
        'time'      => true,
        'timestamp' => true,
        'clob'      => true,
        'blob'      => true,
    );
    /**
     * contains all LOB objects created with this MDB2 instance
    * @var array
    * @access protected
    */
    var $lobs = array();
    // }}}
    // {{{ setResultTypes()
    /**
     * Define the list of types to be associated with the columns of a given
     * result set.
     *
     * This function may be called before invoking fetchRow(), fetchOne()
     * fetchCole() and fetchAll() so that the necessary data type
     * conversions are performed on the data to be retrieved by them. If this
     * function is not called, the type of all result set columns is assumed
     * to be text, thus leading to not perform any conversions.
     *
     * @param resource $result result identifier
     * @param string $types array variable that lists the
     *       data types to be expected in the result set columns. If this array
     *       contains less types than the number of columns that are returned
     *       in the result set, the remaining columns are assumed to be of the
     *       type text. Currently, the types clob and blob are not fully
     *       supported.
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function setResultTypes(&$result, $types)
    {
        $types = is_array($types) ? array_values($types) : array($types);
        foreach ($types as $key => $type) {
            if (!isset($this->valid_types[$type])) {
                $db =& $this->getDBInstance();
                if (PEAR::isError($db)) {
                    return $db;
                }
                return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
                    'setResultTypes: ' . $type . ' for '. $key .' is not a supported column type');
            }
        }
        $result->types = $types;
        return MDB2_OK;
    }
    // }}}
    // {{{ _baseConvertResult()
    /**
     * general type conversion method
     *
     * @param mixed $value refernce to a value to be converted
     * @param int $type constant that specifies which type to convert to
     * @return object a MDB2 error on failure
     * @access protected
     */
    function _baseConvertResult($value, $type)
    {
        switch ($type) {
        case 'text':
            return $value;
        case 'integer':
            return intval($value);
        case 'boolean':
            return $value == 'Y';
        case 'decimal':
            return $value;
        case 'float':
            return doubleval($value);
        case 'date':
            return $value;
        case 'time':
            return $value;
        case 'timestamp':
            return $value;
        case 'clob':
        case 'blob':
            $this->lobs[] = array(
                'buffer' => null,
                'position' => 0,
                'lob_index' => null,
                'endOfLOB' => false,
                'ressource' => $value,
                'value' => null,
            );
            end($this->lobs);
            $lob_index = key($this->lobs);
            $this->lobs[$lob_index]['lob_index'] = $lob_index;
            return fopen('MDB2LOB://'.$lob_index.'@'.$this->db_index, 'r+');
        }
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_INVALID, null, null,
            'attempt to convert result value to an unknown type ' . $type);
    }
    // }}}
    // {{{ convertResult()
    /**
     * convert a value to a RDBMS indepdenant MDB2 type
     *
     * @param mixed $value value to be converted
     * @param int $type constant that specifies which type to convert to
     * @return mixed converted value or a MDB2 error on failure
     * @access public
     */
    function convertResult($value, $type)
    {
        if (is_null($value)) {
            return null;
        }
        return $this->_baseConvertResult($value, $type);
    }
    // }}}
    // {{{ convertResultRow()
    /**
     * convert a result row
     *
     * @param resource $result result identifier
     * @param array $row array with data
     * @return mixed MDB2_OK on success,  a MDB2 error on failure
     * @access public
     */
    function convertResultRow($types, $row)
    {
        if (is_array($types)) {
            $current_column = -1;
            foreach ($row as $key => $column) {
                ++$current_column;
                if (!isset($column) || !isset($types[$current_column])) {
                    continue;
                }
                $value = $this->convertResult($row[$key], $types[$current_column]);
                if (PEAR::isError($value)) {
                    return $value;
                }
                $row[$key] = $value;
            }
        }
        return $row;
    }
    // }}}
    // {{{ getDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare
     * of the given type
     *
     * @param string $type type to which the value should be converted to
     * @param string  $name   name the field to be declared.
     * @param string  $field  definition of the field
     * @return string  DBMS specific SQL code portion that should be used to
     *                 declare the specified field.
     * @access public
     */
    function getDeclaration($type, $name, $field)
    {
        if (!method_exists($this, "_get{$type}Declaration")) {
            $db =& $this->getDBInstance();
            if (PEAR::isError($db)) {
                return $db;
            }
            return $db->raiseError('type not defined: '.$type);
        }
        return $this->{"_get{$type}Declaration"}($name, $field);
    }
    // }}}
    // {{{ _getIntegerDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare an integer type
     * field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       unsigned
     *           Boolean flag that indicates whether the field should be
     *           declared as unsigned integer if possible.
     *
     *       default
     *           Integer value to be used as default for this field.
     *
     *       notnull
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getIntegerDeclaration($name, $field)
    {
        if (array_key_exists('unsigned', $field) && $field['unsigned']) {
            $db =& $this->getDBInstance();
            if (PEAR::isError($db)) {
                return $db;
            }
            $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
        }
        $default = array_key_exists('default', $field) ? ' DEFAULT '.
            $this->quote($field['default'], 'integer') : '';
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        return $name.' INT'.$default.$notnull;
    }
    // }}}
    // {{{ _getTextDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare an text type
     * field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       length
     *           Integer value that determines the maximum length of the text
     *           field. If this argument is missing the field should be
     *           declared to have the longest length allowed by the DBMS.
     *
     *       default
     *           Text value to be used as default for this field.
     *
     *       notnull
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getTextDeclaration($name, $field)
    {
        $default = array_key_exists('default', $field) ? ' DEFAULT '.
            $this->quote($field['default'], 'text') : '';
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        $type = array_key_exists('length', $field) ? 'CHAR ('.$field['length'].')' : 'TEXT';
        return $name.' '.$type.$default.$notnull;
    }
    // }}}
    // {{{ _getCLOBDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare an character
     * large object type field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       length
     *           Integer value that determines the maximum length of the large
     *           object field. If this argument is missing the field should be
     *           declared to have the longest length allowed by the DBMS.
     *
     *       notnull
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getCLOBDeclaration($name, $field)
    {
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        $type = array_key_exists('length', $field) ? 'CHAR ('.$field['length'].')' : 'TEXT';
        return $name.' '.$type.$notnull;
    }
    // }}}
    // {{{ _getBLOBDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare an binary large
     * object type field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       length
     *           Integer value that determines the maximum length of the large
     *           object field. If this argument is missing the field should be
     *           declared to have the longest length allowed by the DBMS.
     *
     *       notnull
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getBLOBDeclaration($name, $field)
    {
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        $type = array_key_exists('length', $field) ? 'CHAR ('.$field['length'].')' : 'TEXT';
        return $name.' '.$type.$notnull;
    }
    // }}}
    // {{{ _getBooleanDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare a boolean type
     * field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       default
     *           Boolean value to be used as default for this field.
     *
     *       notnullL
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getBooleanDeclaration($name, $field)
    {
        $default = array_key_exists('default', $field) ? ' DEFAULT '.
            $this->quote($field['default'], 'boolean') : '';
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        return $name.' CHAR (1)'.$default.$notnull;
    }
    // }}}
    // {{{ _getDateDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare a date type
     * field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       default
     *           Date value to be used as default for this field.
     *
     *       notnull
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getDateDeclaration($name, $field)
    {
        $default = array_key_exists('default', $field) ? ' DEFAULT '.
            $this->quote($field['default'], 'date') : '';
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        return $name.' CHAR ('.strlen('YYYY-MM-DD').')'.$default.$notnull;
    }
    // }}}
    // {{{ _getTimestampDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare a timestamp
     * field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       default
     *           Timestamp value to be used as default for this field.
     *
     *       notnull
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getTimestampDeclaration($name, $field)
    {
        $default = array_key_exists('default', $field) ? ' DEFAULT '.
            $this->quote($field['default'], 'timestamp') : '';
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        return $name.' CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')'.$default.$notnull;
    }
    // }}}
    // {{{ _getTimeDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare a time
     * field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       default
     *           Time value to be used as default for this field.
     *
     *       notnull
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getTimeDeclaration($name, $field)
    {
        $default = array_key_exists('default', $field) ? ' DEFAULT '.
            $this->quote($field['default'], 'time') : '';
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        return $name.' CHAR ('.strlen('HH:MM:SS').')'.$default.$notnull;
    }
    // }}}
    // {{{ _getFloatDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare a float type
     * field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       default
     *           Float value to be used as default for this field.
     *
     *       notnull
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getFloatDeclaration($name, $field)
    {
        $default = array_key_exists('default', $field) ? ' DEFAULT '.
            $this->quote($field['default'], 'float') : '';
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        return $name.' TEXT'.$default.$notnull;
    }
    // }}}
    // {{{ _getDecimalDeclaration()
    /**
     * Obtain DBMS specific SQL code portion needed to declare a decimal type
     * field to be used in statements like CREATE TABLE.
     *
     * @param string $name name the field to be declared.
     * @param array $field associative array with the name of the properties
     *       of the field being declared as array indexes. Currently, the types
     *       of supported field properties are as follows:
     *
     *       default
     *           Decimal value to be used as default for this field.
     *
     *       notnull
     *           Boolean flag that indicates whether this field is constrained
     *           to not be set to null.
     * @return string DBMS specific SQL code portion that should be used to
     *       declare the specified field.
     * @access protected
     */
    function _getDecimalDeclaration($name, $field)
    {
        $default = array_key_exists('default', $field) ? ' DEFAULT '.
            $this->quote($field['default'], 'decimal') : '';
        $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : '';
        return $name.' TEXT'.$default.$notnull;
    }
    // }}}
    // {{{ compareDefinition()
    /**
     * Obtain an array of changes that may need to applied
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access public
     */
    function compareDefinition($current, $previous)
    {
        $type = array_key_exists('type', $current) ? $current['type'] : null;
        if (!method_exists($this, "_compare{$type}Definition")) {
            $db =& $this->getDBInstance();
            if (PEAR::isError($db)) {
                return $db;
            }
            return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
                'type "'.$current['type'].'" is not yet supported');
        }
        if (!array_key_exists('type', $previous) || $previous['type'] != $type) {
            return $current;
        }
        $change = $this->{"_compare{$type}Definition"}($current, $previous);
        $previous_notnull = array_key_exists('notnull', $previous) ? $previous['notnull'] : false;
        $notnull = array_key_exists('notnull', $current) ? $current['notnull'] : false;
        if ($previous_notnull != $notnull) {
            $change['notnull'] = true;
        }
        $previous_default = array_key_exists('default', $previous) ? $previous['default'] :
            ($previous_notnull ? '' : null);
        $default = array_key_exists('default', $current) ? $current['default'] :
            ($notnull ? '' : null);
        if ($previous_default !== $default) {
            $change['default'] = true;
        }
        return $change;
    }
    // }}}
    // {{{ _compareIntegerDefinition()
    /**
     * Obtain an array of changes that may need to applied to an integer field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareIntegerDefinition($current, $previous)
    {
        $change = array();
        $previous_unsigned = array_key_exists('unsigned', $previous) ? $previous['unsigned'] : false;
        $unsigned = array_key_exists('unsigned', $current) ? $current['unsigned'] : false;
        if ($previous_unsigned != $unsigned) {
            $change['unsigned'] = true;
        }
        $previous_autoincrement = array_key_exists('autoincrement', $previous) ? $previous['autoincrement'] : false;
        $autoincrement = array_key_exists('autoincrement', $current) ? $current['autoincrement'] : false;
        if ($previous_autoincrement != $autoincrement) {
            $change['autoincrement'] = true;
        }
        return $change;
    }
    // }}}
    // {{{ _compareTextDefinition()
    /**
     * Obtain an array of changes that may need to applied to an text field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareTextDefinition($current, $previous)
    {
        $change = array();
        $previous_length = array_key_exists('length', $previous) ? $previous['length'] : 0;
        $length = array_key_exists('length', $current) ? $current['length'] : 0;
        if ($previous_length != $length) {
            $change['length'] = true;
        }
        return $change;
    }
    // }}}
    // {{{ _compareCLOBDefinition()
    /**
     * Obtain an array of changes that may need to applied to an CLOB field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareCLOBDefinition($current, $previous)
    {
        return $this->_compareTextDefinition($current, $previous);
    }
    // }}}
    // {{{ _compareBLOBDefinition()
    /**
     * Obtain an array of changes that may need to applied to an BLOB field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareBLOBDefinition($current, $previous)
    {
        return $this->_compareTextDefinition($current, $previous);
    }
    // }}}
    // {{{ _compareDateDefinition()
    /**
     * Obtain an array of changes that may need to applied to an date field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareDateDefinition($current, $previous)
    {
        return array();
    }
    // }}}
    // {{{ _compareTimeDefinition()
    /**
     * Obtain an array of changes that may need to applied to an time field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareTimeDefinition($current, $previous)
    {
        return array();
    }
    // }}}
    // {{{ _compareTimestampDefinition()
    /**
     * Obtain an array of changes that may need to applied to an timestamp field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareTimestampDefinition($current, $previous)
    {
        return array();
    }
    // }}}
    // {{{ _compareBooleanDefinition()
    /**
     * Obtain an array of changes that may need to applied to an boolean field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareBooleanDefinition($current, $previous)
    {
        return array();
    }
    // }}}
    // {{{ _compareFloatDefinition()
    /**
     * Obtain an array of changes that may need to applied to an float field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareFloatDefinition($current, $previous)
    {
        return array();
    }
    // }}}
    // {{{ _compareDecimalDefinition()
    /**
     * Obtain an array of changes that may need to applied to an decimal field
     *
     * @param array $current new definition
     * @param array  $previous old definition
     * @return array  containg all changes that will need to be applied
     * @access protected
     */
    function _compareDecimalDefinition($current, $previous)
    {
        return array();
    }
    // }}}
    // {{{ quote()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param string $value text string value that is intended to be converted.
     * @param string $type type to which the value should be converted to
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access public
     */
    function quote($value, $type = null, $quote = true)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        if (is_null($value)
            || ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)
        ) {
            if (!$quote) {
                return null;
            }
            return 'NULL';
        }
        if (is_null($type)) {
            switch (gettype($value)) {
            case 'integer':
                $type = 'integer';
                break;
            case 'double':
                // todo
                $type = 'decimal';
                $type = 'float';
                break;
            case 'boolean':
                $type = 'boolean';
                break;
            case 'array':
            case 'object':
                 $type = 'text';
                break;
            default:
                if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $value)) {
                    $type = 'timestamp';
                } elseif (preg_match('/^\d{2}:\d{2}$/', $value)) {
                    $type = 'time';
                } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
                    $type = 'date';
                } else {
                    $type = 'text';
                }
                break;
            }
        }
        if (!method_exists($this, "_quote{$type}")) {
            return $db->raiseError('type not defined: '.$type);
        }
        $value = $this->{"_quote{$type}"}($value);
        // ugly hack to remove single quotes
        if (!$quote && isset($value[0]) && $value[0] === "'") {
            $value = substr($value, 1, -1);
        }
        return $value;
    }
    // }}}
    // {{{ _quoteInteger()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param string $value text string value that is intended to be converted.
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteInteger($value)
    {
        return (int)$value;
    }
    // }}}
    // {{{ _quoteText()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param string $value text string value that is intended to be converted.
     * @return string text string that already contains any DBMS specific
     *       escaped character sequences.
     * @access protected
     */
    function _quoteText($value)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return "'".$db->escape($value)."'";
    }
    // }}}
    // {{{ _readFile()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param  $value
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _readFile($value)
    {
        $close = false;
        if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
            $close = true;
            if ($match[1] == 'file://') {
                $value = $match[2];
            }
            $value = @fopen($value, 'r');
        }
        if (is_resource($value)) {
            $db =& $this->getDBInstance();
            if (PEAR::isError($db)) {
                return $db;
            }
            $fp = $value;
            $value = '';
            while (!@feof($fp)) {
                $value.= @fread($fp, $db->options['lob_buffer_length']);
            }
            if ($close) {
                @fclose($fp);
            }
        }
        return $value;
    }
    // }}}
    // {{{ _quoteLOB()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param  $value
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteLOB($value)
    {
        $value = $this->_readFile($value);
        return $this->_quoteText($value);
    }
    // }}}
    // {{{ _quoteCLOB()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param  $value
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteCLOB($value)
    {
        return $this->_quoteLOB($value);
    }
    // }}}
    // {{{ _quoteBLOB()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param  $value
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteBLOB($value)
    {
        return $this->_quoteLOB($value);
    }
    // }}}
    // {{{ _quoteBoolean()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param string $value text string value that is intended to be converted.
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteBoolean($value)
    {
        return ($value ? "'Y'" : "'N'");
    }
    // }}}
    // {{{ _quoteDate()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param string $value text string value that is intended to be converted.
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteDate($value)
    {
       return $this->_quoteText($value);
    }
    // }}}
    // {{{ _quoteTimestamp()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param string $value text string value that is intended to be converted.
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteTimestamp($value)
    {
       return $this->_quoteText($value);
    }
    // }}}
    // {{{ _quoteTime()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     *       compose query statements.
     *
     * @param string $value text string value that is intended to be converted.
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteTime($value)
    {
       return $this->_quoteText($value);
    }
    // }}}
    // {{{ _quoteFloat()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param string $value text string value that is intended to be converted.
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteFloat($value)
    {
       return $this->_quoteText($value);
    }
    // }}}
    // {{{ _quoteDecimal()
    /**
     * Convert a text value into a DBMS specific format that is suitable to
     * compose query statements.
     *
     * @param string $value text string value that is intended to be converted.
     * @return string text string that represents the given argument value in
     *       a DBMS specific format.
     * @access protected
     */
    function _quoteDecimal($value)
    {
       return $this->_quoteText($value);
    }
    // }}}
    // {{{ writeLOBToFile()
    /**
     * retrieve LOB from the database
     *
     * @param resource $lob stream handle
     * @param string $file name of the file into which the LOb should be fetched
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access protected
     */
    function writeLOBToFile($lob, $file)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        $fp = fopen($file, 'wb');
        while (!feof($lob)) {
            $result = fread($lob, $db->options['lob_buffer_length']);
            $read = strlen($result);
            if (fwrite($fp, $result, $read) != $read) {
                fclose($fp);
                return $db->raiseError(MDB2_ERROR, null, null,
                    'writeLOBToFile: could not write to the output file');
            }
        }
        fclose($fp);
        return MDB2_OK;
    }
    // }}}
    // {{{ _retrieveLOB()
    /**
     * retrieve LOB from the database
     *
     * @param resource $lob stream handle
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access protected
     */
    function _retrieveLOB(&$lob)
    {
        if (is_null($lob['value'])) {
            $lob['value'] = $lob['ressource'];
        }
        return MDB2_OK;
    }
    // }}}
    // {{{ readLOB()
    /**
     * Read data from large object input stream.
     *
     * @param resource $lob stream handle
     * @param string $data reference to a variable that will hold data
     *                          to be read from the large object input stream
     * @param integer $length    value that indicates the largest ammount ofdata
     *                          to be read from the large object input stream.
     * @return mixed the effective number of bytes read from the large object
     *                      input stream on sucess or an MDB2 error object.
     * @access public
     * @see endOfLOB()
     */
    function _readLOB($lob, $length)
    {
        return substr($lob['value'], $lob['position'], $length);
    }
    // }}}
    // {{{ _endOfLOB()
    /**
     * Determine whether it was reached the end of the large object and
     * therefore there is no more data to be read for the its input stream.
     *
     * @param resource $lob stream handle
     * @return mixed true or false on success, a MDB2 error on failure
     * @access protected
     */
    function _endOfLOB($lob)
    {
        return $lob['endOfLOB'];
    }
    // }}}
    // {{{ destroyLOB()
    /**
     * Free any resources allocated during the lifetime of the large object
     * handler object.
     *
     * @param resource $lob stream handle
     * @access public
     */
    function destroyLOB($lob)
    {
        $lob_data = stream_get_meta_data($lob);
        $lob_index = $lob_data['wrapper_data']->lob_index;
        fclose($lob);
        if (isset($this->lobs[$lob_index])) {
            $this->_destroyLOB($lob_index);
            unset($this->lobs[$lob_index]);
        }
        return MDB2_OK;
    }
    // }}}
    // {{{ _destroyLOB()
    /**
     * Free any resources allocated during the lifetime of the large object
     * handler object.
     *
     * @param int $lob_index from the lob array
     * @access private
     */
    function _destroyLOB($lob_index)
    {
        return MDB2_OK;
    }
    // }}}
    // {{{ implodeArray()
    /**
     * apply a type to all values of an array and return as a comma seperated string
     * useful for generating IN statements
     *
     * @access public
     *
     * @param array $array data array
     * @param string $type determines type of the field
     *
     * @return string comma seperated values
     */
    function implodeArray($array, $type = false)
    {
        if (!is_array($array) || empty($array)) {
            return 'NULL';
        }
        if ($type) {
            foreach ($array as $value) {
                $return[] = $this->quote($value, $type);
            }
        } else {
            $return = $array;
        }
        return implode(', ', $return);
    }
}
?>
program/lib/MDB2/Driver/Manager/Common.php
New file
@@ -0,0 +1,616 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5                                                 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
// | Stig. S. Bakken, Lukas Smith                                         |
// | All rights reserved.                                                 |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
// | API as well as database abstraction for PHP applications.            |
// | This LICENSE is in the BSD license style.                            |
// |                                                                      |
// | Redistribution and use in source and binary forms, with or without   |
// | modification, are permitted provided that the following conditions   |
// | are met:                                                             |
// |                                                                      |
// | Redistributions of source code must retain the above copyright       |
// | notice, this list of conditions and the following disclaimer.        |
// |                                                                      |
// | Redistributions in binary form must reproduce the above copyright    |
// | notice, this list of conditions and the following disclaimer in the  |
// | documentation and/or other materials provided with the distribution. |
// |                                                                      |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission.                                                  |
// |                                                                      |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
// | POSSIBILITY OF SUCH DAMAGE.                                          |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org>                           |
// +----------------------------------------------------------------------+
//
// $Id$
//
/**
 * @package  MDB2
 * @category Database
 * @author   Lukas Smith <smith@pooteeweet.org>
 */
/**
 * Base class for the management modules that is extended by each MDB2 driver
 *
 * @package MDB2
 * @category Database
 * @author  Lukas Smith <smith@pooteeweet.org>
 */
class MDB2_Driver_Manager_Common extends MDB2_Module_Common
{
    // }}}
    // {{{ getFieldDeclarationList()
    /**
     * get declaration of a number of field in bulk
     *
     * @param string $fields  a multidimensional associative array.
     *      The first dimension determines the field name, while the second
     *      dimension is keyed with the name of the properties
     *      of the field being declared as array indexes. Currently, the types
     *      of supported field properties are as follows:
     *
     *      default
     *          Boolean value to be used as default for this field.
     *
     *      notnull
     *          Boolean flag that indicates whether this field is constrained
     *          to not be set to null.
     *
     * @return mixed string on success, a MDB2 error on failure
     * @access public
     */
    function getFieldDeclarationList($fields)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        if (is_array($fields)) {
            foreach ($fields as $field_name => $field) {
                $query = $db->getDeclaration($field['type'], $field_name, $field);
                if (PEAR::isError($query)) {
                    return $query;
                }
                $query_fields[] = $query;
            }
            return implode(',', $query_fields);
        }
        return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
            'getFieldDeclarationList: the definition of the table "'.$table_name.'" does not contain any fields');
    }
    // }}}
    // {{{ _isSequenceName()
    /**
     * list all tables in the current database
     *
     * @param string $sqn string that containts name of a potential sequence
     * @return mixed name of the sequence if $sqn is a name of a sequence, else false
     * @access protected
     */
    function _isSequenceName($sqn)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        $seq_pattern = '/^'.preg_replace('/%s/', '([a-z0-9_]+)', $db->options['seqname_format']).'$/i';
        $seq_name = preg_replace($seq_pattern, '\\1', $sqn);
        if ($seq_name && $sqn == $db->getSequenceName($seq_name)) {
            return $seq_name;
        }
        return false;
    }
    // }}}
    // {{{ createDatabase()
    /**
     * create a new database
     *
     * @param string $name name of the database that should be created
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function createDatabase($database)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'createDatabase: database creation is not supported');
    }
    // }}}
    // {{{ dropDatabase()
    /**
     * drop an existing database
     *
     * @param string $name name of the database that should be dropped
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function dropDatabase($database)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'dropDatabase: database dropping is not supported');
    }
    // }}}
    // {{{ createTable()
    /**
     * create a new table
     *
     * @param string $name     Name of the database that should be created
     * @param array $fields Associative array that contains the definition of each field of the new table
     *                        The indexes of the array entries are the names of the fields of the table an
     *                        the array entry values are associative arrays like those that are meant to be
     *                         passed with the field definitions to get[Type]Declaration() functions.
     *
     *                        Example
     *                        array(
     *
     *                            'id' => array(
     *                                'type' => 'integer',
     *                                'unsigned' => 1
     *                                'notnull' => 1
     *                                'default' => 0
     *                            ),
     *                            'name' => array(
     *                                'type' => 'text',
     *                                'length' => 12
     *                            ),
     *                            'password' => array(
     *                                'type' => 'text',
     *                                'length' => 12
     *                            )
     *                        );
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function createTable($name, $fields)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        if (!$name) {
            return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
                'createTable: no valid table name specified');
        }
        if (empty($fields)) {
            return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
                'createTable: no fields specified for table "'.$name.'"');
        }
        $query_fields = $this->getFieldDeclarationList($fields);
        if (PEAR::isError($query_fields)) {
            return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
                'createTable: unkown error');
        }
        $query = "CREATE TABLE $name ($query_fields)";
        return $db->query($query);
    }
    // }}}
    // {{{ dropTable()
    /**
     * drop an existing table
     *
     * @param string $name name of the table that should be dropped
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function dropTable($name)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->query("DROP TABLE $name");
    }
    // }}}
    // {{{ alterTable()
    /**
     * alter an existing table
     *
     * @param string $name         name of the table that is intended to be changed.
     * @param array $changes     associative array that contains the details of each type
     *                             of change that is intended to be performed. The types of
     *                             changes that are currently supported are defined as follows:
     *
     *                             name
     *
     *                                New name for the table.
     *
     *                            add
     *
     *                                Associative array with the names of fields to be added as
     *                                 indexes of the array. The value of each entry of the array
     *                                 should be set to another associative array with the properties
     *                                 of the fields to be added. The properties of the fields should
     *                                 be the same as defined by the Metabase parser.
     *
     *
     *                            remove
     *
     *                                Associative array with the names of fields to be removed as indexes
     *                                 of the array. Currently the values assigned to each entry are ignored.
     *                                 An empty array should be used for future compatibility.
     *
     *                            rename
     *
     *                                Associative array with the names of fields to be renamed as indexes
     *                                 of the array. The value of each entry of the array should be set to
     *                                 another associative array with the entry named name with the new
     *                                 field name and the entry named Declaration that is expected to contain
     *                                 the portion of the field declaration already in DBMS specific SQL code
     *                                 as it is used in the CREATE TABLE statement.
     *
     *                            change
     *
     *                                Associative array with the names of the fields to be changed as indexes
     *                                 of the array. Keep in mind that if it is intended to change either the
     *                                 name of a field and any other properties, the change array entries
     *                                 should have the new names of the fields as array indexes.
     *
     *                                The value of each entry of the array should be set to another associative
     *                                 array with the properties of the fields to that are meant to be changed as
     *                                 array entries. These entries should be assigned to the new values of the
     *                                 respective properties. The properties of the fields should be the same
     *                                 as defined by the Metabase parser.
     *
     *                            Example
     *                                array(
     *                                    'name' => 'userlist',
     *                                    'add' => array(
     *                                        'quota' => array(
     *                                            'type' => 'integer',
     *                                            'unsigned' => 1
     *                                        )
     *                                    ),
     *                                    'remove' => array(
     *                                        'file_limit' => array(),
     *                                        'time_limit' => array()
     *                                        ),
     *                                    'change' => array(
     *                                        'gender' => array(
     *                                            'default' => 'M',
     *                                        )
     *                                    ),
     *                                    'rename' => array(
     *                                        'sex' => array(
     *                                            'name' => 'gender',
     *                                        )
     *                                    )
     *                                )
     * @param boolean $check     indicates whether the function should just check if the DBMS driver
     *                             can perform the requested table alterations if the value is true or
     *                             actually perform them otherwise.
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function alterTable($name, $changes, $check)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'alterTable: database table alterations are not supported');
    }
    // }}}
    // {{{ listDatabases()
    /**
     * list all databases
     *
     * @return mixed data array on success, a MDB2 error on failure
     * @access public
     */
    function listDatabases()
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'listDatabases: list databases is not supported');
    }
    // }}}
    // {{{ listUsers()
    /**
     * list all users
     *
     * @return mixed data array on success, a MDB2 error on failure
     * @access public
     */
    function listUsers()
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'listUsers: list user is not supported');
    }
    // }}}
    // {{{ listViews()
    /**
     * list all views in the current database
     *
     * @return mixed data array on success, a MDB2 error on failure
     * @access public
     */
    function listViews()
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'listViews: list view is not supported');
    }
    // }}}
    // {{{ listFunctions()
    /**
     * list all functions in the current database
     *
     * @return mixed data array on success, a MDB2 error on failure
     * @access public
     */
    function listFunctions()
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'listFunctions: list function is not supported');
    }
    // }}}
    // {{{ listTables()
    /**
     * list all tables in the current database
     *
     * @return mixed data array on success, a MDB2 error on failure
     * @access public
     */
    function listTables()
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'listTables: list tables is not supported');
    }
    // }}}
    // {{{ listTableFields()
    /**
     * list all fields in a tables in the current database
     *
     * @param string $table name of table that should be used in method
     * @return mixed data array on success, a MDB2 error on failure
     * @access public
     */
    function listTableFields($table)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'listTableFields: list table fields is not supported');
    }
    // }}}
    // {{{ createIndex()
    /**
     * get the stucture of a field into an array
     *
     * @param string    $table         name of the table on which the index is to be created
     * @param string    $name         name of the index to be created
     * @param array     $definition        associative array that defines properties of the index to be created.
     *                                 Currently, only one property named FIELDS is supported. This property
     *                                 is also an associative with the names of the index fields as array
     *                                 indexes. Each entry of this array is set to another type of associative
     *                                 array that specifies properties of the index that are specific to
     *                                 each field.
     *
     *                                Currently, only the sorting property is supported. It should be used
     *                                 to define the sorting direction of the index. It may be set to either
     *                                 ascending or descending.
     *
     *                                Not all DBMS support index sorting direction configuration. The DBMS
     *                                 drivers of those that do not support it ignore this property. Use the
     *                                 function supports() to determine whether the DBMS driver can manage indexes.
     *
     *                                 Example
     *                                    array(
     *                                        'fields' => array(
     *                                            'user_name' => array(
     *                                                'sorting' => 'ascending'
     *                                            ),
     *                                            'last_login' => array()
     *                                        )
     *                                    )
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function createIndex($table, $name, $definition)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'createIndex: Creating Indexes is not supported');
    }
    // }}}
    // {{{ dropIndex()
    /**
     * drop existing index
     *
     * @param string    $table         name of table that should be used in method
     * @param string    $name         name of the index to be dropped
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function dropIndex($table, $name)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->query("DROP INDEX $name");
    }
    // }}}
    // {{{ listTableIndexes()
    /**
     * list all indexes in a table
     *
     * @param string    $table      name of table that should be used in method
     * @return mixed data array on success, a MDB2 error on failure
     * @access public
     */
    function listTableIndexes($table)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'listTableIndexes: List Indexes is not supported');
    }
    // }}}
    // {{{ createSequence()
    /**
     * create sequence
     *
     * @param string    $seq_name     name of the sequence to be created
     * @param string    $start         start value of the sequence; default is 1
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function createSequence($seq_name, $start = 1)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'createSequence: sequence creation not supported');
    }
    // }}}
    // {{{ dropSequence()
    /**
     * drop existing sequence
     *
     * @param string    $seq_name     name of the sequence to be dropped
     * @return mixed MDB2_OK on success, a MDB2 error on failure
     * @access public
     */
    function dropSequence($name)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'dropSequence: sequence dropping not supported');
    }
    // }}}
    // {{{ listSequences()
    /**
     * list all sequences in the current database
     *
     * @return mixed data array on success, a MDB2 error on failure
     * @access public
     */
    function listSequences()
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
            'listSequences: List sequences is not supported');
    }
}
?>
program/lib/MDB2/Extended.php
New file
@@ -0,0 +1,605 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5                                                 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
// | Stig. S. Bakken, Lukas Smith                                         |
// | All rights reserved.                                                 |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
// | API as well as database abstraction for PHP applications.            |
// | This LICENSE is in the BSD license style.                            |
// |                                                                      |
// | Redistribution and use in source and binary forms, with or without   |
// | modification, are permitted provided that the following conditions   |
// | are met:                                                             |
// |                                                                      |
// | Redistributions of source code must retain the above copyright       |
// | notice, this list of conditions and the following disclaimer.        |
// |                                                                      |
// | Redistributions in binary form must reproduce the above copyright    |
// | notice, this list of conditions and the following disclaimer in the  |
// | documentation and/or other materials provided with the distribution. |
// |                                                                      |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission.                                                  |
// |                                                                      |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
// | POSSIBILITY OF SUCH DAMAGE.                                          |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org>                           |
// +----------------------------------------------------------------------+
//
// $Id$
/**
 * @package  MDB2
 * @category Database
 * @author   Lukas Smith <smith@pooteeweet.org>
 */
/**
 * Used by autoPrepare()
 */
define('MDB2_AUTOQUERY_INSERT', 1);
define('MDB2_AUTOQUERY_UPDATE', 2);
/**
 * MDB2_Extended: class which adds several high level methods to MDB2
 *
 * @package MDB2
 * @category Database
 * @author Lukas Smith <smith@pooteeweet.org>
 */
class MDB2_Extended extends MDB2_Module_Common
{
    // }}}
    // {{{ autoPrepare()
    /**
     * Make automaticaly an insert or update query and call prepare() with it
     *
     * @param string $table name of the table
     * @param array $table_fields ordered array containing the fields names
     * @param int $mode type of query to make (MDB2_AUTOQUERY_INSERT or MDB2_AUTOQUERY_UPDATE)
     * @param string $where in case of update queries, this string will be put after the sql WHERE statement
     * @return resource handle for the query
     * @param mixed   $types  array that contains the types of the placeholders
     * @param mixed   $result_types  array that contains the types of the columns in
     *                        the result set
     * @see buildManipSQL
     * @access public
     */
    function autoPrepare($table, $table_fields, $mode = MDB2_AUTOQUERY_INSERT,
        $where = false, $types = null, $result_types = null)
    {
        $query = $this->buildManipSQL($table, $table_fields, $mode, $where);
        if (PEAR::isError($query)) {
            return $query;
        }
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        return $db->prepare($query, $types, $result_types);
    }
    // {{{
    // }}} autoExecute()
    /**
     * Make automaticaly an insert or update query and call prepare() and execute() with it
     *
     * @param string $table name of the table
     * @param array $fields_values assoc ($key=>$value) where $key is a field name and $value its value
     * @param int $mode type of query to make (MDB2_AUTOQUERY_INSERT or MDB2_AUTOQUERY_UPDATE)
     * @param string $where in case of update queries, this string will be put after the sql WHERE statement
     * @param mixed   $types  array that contains the types of the placeholders
     * @param mixed   $result_types  array that contains the types of the columns in
     *                        the result set
     * @param mixed $result_class string which specifies which result class to use
     * @return mixed  a new MDB2_Result or a MDB2 Error Object when fail
     * @see buildManipSQL
     * @see autoPrepare
     * @access public
    */
    function &autoExecute($table, $fields_values, $mode = MDB2_AUTOQUERY_INSERT,
        $where = false, $types = null, $result_types = null, $result_class = true)
    {
        $stmt = $this->autoPrepare($table, array_keys($fields_values), $mode, $where, $types, $result_types);
        if (PEAR::isError($stmt)) {
            return $stmt;
        }
        $params = array_values($fields_values);
        $stmt->bindParamArray($params);
        $result =& $stmt->execute($result_class);
        $stmt->free();
        return $result;
    }
    // {{{
    // }}} buildManipSQL()
    /**
     * Make automaticaly an sql query for prepare()
     *
     * Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), MDB2_AUTOQUERY_INSERT)
     *           will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
     * NB : - This belongs more to a SQL Builder class, but this is a simple facility
     *      - Be carefull ! If you don't give a $where param with an UPDATE query, all
     *        the records of the table will be updated !
     *
     * @param string $table name of the table
     * @param array $table_fields ordered array containing the fields names
     * @param int $mode type of query to make (MDB2_AUTOQUERY_INSERT or MDB2_AUTOQUERY_UPDATE)
     * @param string $where in case of update queries, this string will be put after the sql WHERE statement
     * @return string sql query for prepare()
     * @access public
     */
    function buildManipSQL($table, $table_fields, $mode, $where = false)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        if (count($table_fields) == 0) {
            return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA);
        }
        switch ($mode) {
        case MDB2_AUTOQUERY_INSERT:
            $cols = implode(', ', $table_fields);
            $values = '?'.str_repeat(', ?', count($table_fields)-1);
            return 'INSERT INTO '.$table.' ('.$cols.') VALUES ('.$values.')';
            break;
        case MDB2_AUTOQUERY_UPDATE:
            $set = implode(' = ?, ', $table_fields).' = ?';
            $sql = 'UPDATE '.$table.' SET '.$set;
            if ($where !== false) {
                $sql.= ' WHERE '.$where;
            }
            return $sql;
            break;
        }
        return $db->raiseError(MDB2_ERROR_SYNTAX);
    }
    // {{{
    // }}} limitQuery()
    /**
     * Generates a limited query
     *
     * @param string $query query
     * @param mixed   $types  array that contains the types of the columns in
     *                        the result set
     * @param integer $from the row to start to fetching
     * @param integer $count the numbers of rows to fetch
     * @param mixed $result_class string which specifies which result class to use
     * @return mixed a valid ressource pointer or a MDB2 Error Object
     * @access public
     */
    function &limitQuery($query, $types, $count, $from = 0, $result_class = true)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        $result = $db->setLimit($count, $from);
        if (PEAR::isError($result)) {
            return $result;
        }
        $result =& $db->query($query, $types, $result_class);
        return $result;
    }
    // {{{
    // }}} getOne()
    /**
     * Fetch the first column of the first row of data returned from
     * a query.  Takes care of doing the query and freeing the results
     * when finished.
     *
     * @param string $query the SQL query
     * @param string $type string that contains the type of the column in the
     *       result set
     * @param array $params if supplied, prepare/execute will be used
     *       with this array as execute parameters
     * @param array $param_types array that contains the types of the values
     *       defined in $params
     * @param mixed $colnum which column to return
     * @return mixed MDB2_OK or data on success, a MDB2 error on failure
     * @access public
     */
    function getOne($query, $type = null, $params = array(),
        $param_types = null, $colnum = 0)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        settype($params, 'array');
        settype($type, 'array');
        if (count($params) == 0) {
            return $db->queryOne($query, $type, $colnum);
        }
        $stmt = $db->prepare($query, $param_types, $type);
        if (PEAR::isError($stmt)) {
            return $stmt;
        }
        $stmt->bindParamArray($params);
        $result = $stmt->execute();
        if (!MDB2::isResultCommon($result)) {
            return $result;
        }
        $one = $result->fetchOne($colnum);
        $stmt->free();
        $result->free();
        return $one;
    }
    // }}}
    // {{{ getRow()
    /**
     * Fetch the first row of data returned from a query.  Takes care
     * of doing the query and freeing the results when finished.
     *
     * @param string $query the SQL query
     * @param array $types array that contains the types of the columns in
     *       the result set
     * @param array $params array if supplied, prepare/execute will be used
     *       with this array as execute parameters
     * @param array $param_types array that contains the types of the values
     *       defined in $params
     * @param integer $fetchmode the fetch mode to use
     * @return mixed MDB2_OK or data array on success, a MDB2 error on failure
     * @access public
     */
    function getRow($query, $types = null, $params = array(),
        $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        settype($params, 'array');
        if (count($params) == 0) {
            return $db->queryRow($query, $types, $fetchmode);
        }
        $stmt = $db->prepare($query, $param_types, $types);
        if (PEAR::isError($stmt)) {
            return $stmt;
        }
        $stmt->bindParamArray($params);
        $result = $stmt->execute();
        if (!MDB2::isResultCommon($result)) {
            return $result;
        }
        $row = $result->fetchRow($fetchmode);
        $stmt->free();
        $result->free();
        return $row;
    }
    // }}}
    // {{{ getCol()
    /**
     * Fetch a single column from a result set and return it as an
     * indexed array.
     *
     * @param string $query the SQL query
     * @param string $type string that contains the type of the column in the
     *       result set
     * @param array $params array if supplied, prepare/execute will be used
     *       with this array as execute parameters
     * @param array $param_types array that contains the types of the values
     *       defined in $params
     * @param mixed $colnum which column to return
     * @return mixed MDB2_OK or data array on success, a MDB2 error on failure
     * @access public
     */
    function getCol($query, $type = null, $params = array(),
        $param_types = null, $colnum = 0)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        settype($params, 'array');
        settype($type, 'array');
        if (count($params) == 0) {
            return $db->queryCol($query, $type, $colnum);
        }
        $stmt = $db->prepare($query, $param_types, $type);
        if (PEAR::isError($stmt)) {
            return $stmt;
        }
        $stmt->bindParamArray($params);
        $result = $stmt->execute();
        if (!MDB2::isResultCommon($result)) {
            return $result;
        }
        $col = $result->fetchCol($colnum);
        $stmt->free();
        $result->free();
        return $col;
    }
    // }}}
    // {{{ getAll()
    /**
     * Fetch all the rows returned from a query.
     *
     * @param string $query the SQL query
     * @param array $types array that contains the types of the columns in
     *       the result set
     * @param array $params array if supplied, prepare/execute will be used
     *       with this array as execute parameters
     * @param array $param_types array that contains the types of the values
     *       defined in $params
     * @param integer $fetchmode the fetch mode to use
     * @param boolean $rekey if set to true, the $all will have the first
     *       column as its first dimension
     * @param boolean $force_array used only when the query returns exactly
     *       two columns. If true, the values of the returned array will be
     *       one-element arrays instead of scalars.
     * @param boolean $group if true, the values of the returned array is
     *       wrapped in another array.  If the same key value (in the first
     *       column) repeats itself, the values will be appended to this array
     *       instead of overwriting the existing values.
     * @return mixed MDB2_OK or data array on success, a MDB2 error on failure
     * @access public
     */
    function getAll($query, $types = null, $params = array(),
        $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT,
        $rekey = false, $force_array = false, $group = false)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        settype($params, 'array');
        if (count($params) == 0) {
            return $db->queryAll($query, $types, $fetchmode, $rekey, $force_array, $group);
        }
        $stmt = $db->prepare($query, $param_types, $types);
        if (PEAR::isError($stmt)) {
            return $stmt;
        }
        $stmt->bindParamArray($params);
        $result = $stmt->execute();
        if (!MDB2::isResultCommon($result)) {
            return $result;
        }
        $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group);
        $stmt->free();
        $result->free();
        return $all;
    }
    // }}}
    // {{{ getAssoc()
    /**
     * Fetch the entire result set of a query and return it as an
     * associative array using the first column as the key.
     *
     * If the result set contains more than two columns, the value
     * will be an array of the values from column 2-n.  If the result
     * set contains only two columns, the returned value will be a
     * scalar with the value of the second column (unless forced to an
     * array with the $force_array parameter).  A MDB error code is
     * returned on errors.  If the result set contains fewer than two
     * columns, a MDB2_ERROR_TRUNCATED error is returned.
     *
     * For example, if the table 'mytable' contains:
     *
     *   ID      TEXT       DATE
     * --------------------------------
     *   1       'one'      944679408
     *   2       'two'      944679408
     *   3       'three'    944679408
     *
     * Then the call getAssoc('SELECT id,text FROM mytable') returns:
     *    array(
     *      '1' => 'one',
     *      '2' => 'two',
     *      '3' => 'three',
     *    )
     *
     * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
     *    array(
     *      '1' => array('one', '944679408'),
     *      '2' => array('two', '944679408'),
     *      '3' => array('three', '944679408')
     *    )
     *
     * If the more than one row occurs with the same value in the
     * first column, the last row overwrites all previous ones by
     * default.  Use the $group parameter if you don't want to
     * overwrite like this.  Example:
     *
     * getAssoc('SELECT category,id,name FROM mytable', null, null
     *           MDB2_FETCHMODE_ASSOC, false, true) returns:
     *    array(
     *      '1' => array(array('id' => '4', 'name' => 'number four'),
     *                   array('id' => '6', 'name' => 'number six')
     *             ),
     *      '9' => array(array('id' => '4', 'name' => 'number four'),
     *                   array('id' => '6', 'name' => 'number six')
     *             )
     *    )
     *
     * Keep in mind that database functions in PHP usually return string
     * values for results regardless of the database's internal type.
     *
     * @param string $query the SQL query
     * @param array $types array that contains the types of the columns in
     *       the result set
     * @param array $params array if supplied, prepare/execute will be used
     *       with this array as execute parameters
     * @param array $param_types array that contains the types of the values
     *       defined in $params
     * @param boolean $force_array used only when the query returns
     * exactly two columns.  If TRUE, the values of the returned array
     * will be one-element arrays instead of scalars.
     * @param boolean $group if TRUE, the values of the returned array
     *       is wrapped in another array.  If the same key value (in the first
     *       column) repeats itself, the values will be appended to this array
     *       instead of overwriting the existing values.
     * @return array associative array with results from the query.
     * @access public
     */
    function getAssoc($query, $types = null, $params = array(), $param_types = null,
        $fetchmode = MDB2_FETCHMODE_DEFAULT, $force_array = false, $group = false)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        settype($params, 'array');
        if (count($params) == 0) {
            return $db->queryAll($query, $types, $fetchmode, true, $force_array, $group);
        }
        $stmt = $db->prepare($query, $param_types, $types);
        if (PEAR::isError($stmt)) {
            return $stmt;
        }
        $stmt->bindParamArray($params);
        $result = $stmt->execute();
        if (!MDB2::isResultCommon($result)) {
            return $result;
        }
        $all = $result->fetchAll($fetchmode, true, $force_array, $group);
        $stmt->free();
        $result->free();
        return $all;
    }
    // }}}
    // {{{ executeMultiple()
    /**
     * This function does several execute() calls on the same statement handle.
     * $params must be an array indexed numerically from 0, one execute call is
     * done for every 'row' in the array.
     *
     * If an error occurs during execute(), executeMultiple() does not execute
     * the unfinished rows, but rather returns that error.
     *
     * @param resource $stmt query handle from prepare()
     * @param array $params numeric array containing the
     *        data to insert into the query
     * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure
     * @access public
     * @see prepare(), execute()
     */
    function executeMultiple(&$stmt, $params = null)
    {
        for ($i = 0, $j = count($params); $i < $j; $i++) {
            $stmt->bindParamArray($params[$i]);
            $result = $stmt->execute();
            if (PEAR::isError($result)) {
                return $result;
            }
        }
        return MDB2_OK;
    }
    // }}}
    // {{{ getBeforeID()
    /**
     * returns the next free id of a sequence if the RDBMS
     * does not support auto increment
     *
     * @param string $table name of the table into which a new row was inserted
     * @param boolean $ondemand when true the seqence is
     *                          automatic created, if it
     *                          not exists
     *
     * @return mixed MDB2 Error Object or id
     * @access public
     */
    function getBeforeID($table, $field, $ondemand = true)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        if ($db->supports('auto_increment') !== true) {
            $seq = $table.(empty($field) ? '' : '_'.$field);
            $id = $db->nextID($seq, $ondemand);
            if (PEAR::isError($id)) {
                return $id;
            }
            return $db->quote($id, 'integer');
        }
        return 'NULL';
    }
    // }}}
    // {{{ getAfterID()
    /**
     * returns the autoincrement ID if supported or $id
     *
     * @param mixed $id value as returned by getBeforeId()
     * @param string $table name of the table into which a new row was inserted
     * @return mixed MDB2 Error Object or id
     * @access public
     */
    function getAfterID($id, $table, $field)
    {
        $db =& $this->getDBInstance();
        if (PEAR::isError($db)) {
            return $db;
        }
        if ($db->supports('auto_increment') !== true) {
            return $id;
        }
        return $db->lastInsertID($table, $field);
    }
}
?>
program/lib/MDB2/Iterator.php
New file
@@ -0,0 +1,285 @@
<?php
// +----------------------------------------------------------------------+
// | PHP version 5                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
// | Stig. S. Bakken, Lukas Smith                                         |
// | All rights reserved.                                                 |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
// | API as well as database abstraction for PHP applications.            |
// | This LICENSE is in the BSD license style.                            |
// |                                                                      |
// | Redistribution and use in source and binary forms, with or without   |
// | modification, are permitted provided that the following conditions   |
// | are met:                                                             |
// |                                                                      |
// | Redistributions of source code must retain the above copyright       |
// | notice, this list of conditions and the following disclaimer.        |
// |                                                                      |
// | Redistributions in binary form must reproduce the above copyright    |
// | notice, this list of conditions and the following disclaimer in the  |
// | documentation and/or other materials provided with the distribution. |
// |                                                                      |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission.                                                  |
// |                                                                      |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
// | POSSIBILITY OF SUCH DAMAGE.                                          |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org>                           |
// +----------------------------------------------------------------------+
//
// $Id$
/**
 * @package  MDB2
 * @category Database
 * @author   Lukas Smith <smith@pooteeweet.org>
 */
class MDB2_Iterator implements Iterator
{
    protected $fetchmode;
    protected $result;
    protected $row;
    // {{{ constructor
    /**
     * Constructor
     */
    public function __construct($result, $fetchmode = MDB2_FETCHMODE_DEFAULT)
    {
        $this->result = $result;
        $this->fetchmode = $fetchmode;
    }
    // }}}
    // {{{ seek()
    /**
    * seek forward to a specific row in a result set
    *
    * @param int    $rownum    number of the row where the data can be found
    * @return void
    * @access public
    */
    public function seek($rownum)
    {
        $this->row = null;
        if ($this->result) {
            $this->result->seek($rownum);
        }
    }
    // }}}
    // {{{ next()
    /**
    * Fetch next row of data
    *
    * @return void
    * @access public
    */
    public function next()
    {
        $this->row = null;
    }
    // }}}
    // {{{ current()
    /**
     * return a row of data
     *
    * @return void
    * @access public
    */
    public function current()
    {
        if (is_null($this->row)) {
            $row = $this->result->fetchRow($this->fetchmode);
            if (PEAR::isError($row)) {
                $row = false;
            }
            $this->row = $row;
        }
        return $this->row;
    }
    // }}}
    // {{{ valid()
    /**
    * check if the end of the result set has been reached
    *
    * @return mixed true or false on sucess, a MDB2 error on failure
    * @access public
    */
    public function valid()
    {
        return (bool)$this->current();
    }
    // }}}
    // {{{ free()
    /**
     * Free the internal resources associated with result.
     *
     * @return boolean true on success, false if result is invalid
     * @access public
     */
    public function free()
    {
        if ($this->result) {
            return $this->result->free();
        }
        $this->result = null;
        $this->row = null;
        return false;
    }
    // }}}
    // {{{ key()
    /**
    * nothing, but Iterator wants to implement this.
    *
    * @return void
    * @access public
    */
    public function key()
    {
        if ($this->result) {
            return $this->result->rowCount();
        }
        return false;
    }
    // }}}
    // {{{ rewind()
    /**
    * seek to the first row in a result set
    *
    * @return void
    * @access public
    */
    public function rewind()
    {
    }
    // }}}
    // {{{ destructor
    /**
     * Destructor
     */
    public function __destruct()
    {
        $this->free();
    }
}
class MDB2_BufferedIterator extends MDB2_Iterator implements SeekableIterator
{
    // }}}
    // {{{ valid()
    /**
    * check if the end of the result set has been reached
    *
    * @return mixed true or false on sucess, a MDB2 error on failure
    * @access public
    */
    public function valid()
    {
        if ($this->result) {
            return $this->result->valid();
        }
        return false;
    }
    // }}}
    // {{{count()
    /**
     * returns the number of rows in a result object
     *
     * @return mixed MDB2 Error Object or the number of rows
     * @access public
     */
    public function count()
    {
        if ($this->result) {
            return $this->result->numRows();
        }
        return false;
    }
    // }}}
    // {{{ hasPrev()
    /**
    * check if there is a previous row
    *
    * @return mixed true or false on sucess, a MDB2 error on failure
    * @access public
    */
    public function hasPrev()
    {
        if ($this->result) {
            return $this->result->rowCount() > 0;
        }
        return false;
    }
    // }}}
    // {{{ rewind()
    /**
    * seek to the first row in a result set
    *
    * @return mixed MDB2_OK on success, a MDB2 error on failure
    * @access public
    */
    public function rewind()
    {
        $this->seek(0);
    }
    // }}}
    // {{{ prev()
    /**
    * move internal row point to the previous row
    * Fetch and return a row of data
    *
    * @return void
    * @access public
    */
    public function prev()
    {
        if ($this->hasPrev()) {
            $this->seek($this->result->rowCount() - 1);
        } else {
            return false;
        }
        return $this->next();
    }
}
?>
program/lib/MDB2/LOB.php
New file
@@ -0,0 +1,146 @@
<?php
// +----------------------------------------------------------------------+
// | PHP version 5                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
// | Stig. S. Bakken, Lukas Smith                                         |
// | All rights reserved.                                                 |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
// | API as well as database abstraction for PHP applications.            |
// | This LICENSE is in the BSD license style.                            |
// |                                                                      |
// | Redistribution and use in source and binary forms, with or without   |
// | modification, are permitted provided that the following conditions   |
// | are met:                                                             |
// |                                                                      |
// | Redistributions of source code must retain the above copyright       |
// | notice, this list of conditions and the following disclaimer.        |
// |                                                                      |
// | Redistributions in binary form must reproduce the above copyright    |
// | notice, this list of conditions and the following disclaimer in the  |
// | documentation and/or other materials provided with the distribution. |
// |                                                                      |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission.                                                  |
// |                                                                      |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
// | POSSIBILITY OF SUCH DAMAGE.                                          |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org>                           |
// +----------------------------------------------------------------------+
//
// $Id$
/**
 * @package  MDB2
 * @category Database
 * @author   Lukas Smith <smith@pooteeweet.org>
 */
require_once 'MDB2.php';
class MDB2_LOB
{
    var $db_index;
    var $lob_index;
    var $lob;
    function stream_open($path, $mode, $options, &$opened_path)
    {
        if (!preg_match('/^rb?\+?$/', $mode)) {
            return false;
        }
        $url = parse_url($path);
        if (!array_key_exists('host', $url) && !array_key_exists('user', $url)) {
            return false;
        }
        $this->db_index = $url['host'];
        if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
            return false;
        }
        $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
        $this->lob_index = $url['user'];
        if (!isset($db->datatype->lobs[$this->lob_index])) {
            return false;
        }
        $this->lob =& $db->datatype->lobs[$this->lob_index];
        $db->datatype->_retrieveLOB($this->lob);
        return true;
    }
    function stream_read($count)
    {
        if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
            $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
            $data = $db->datatype->_readLOB($this->lob, $count);
            $length = strlen($data);
            if ($length == 0) {
                $this->lob['endOfLOB'] = true;
            }
            $this->lob['position'] += $length;
            return $data;
        }
   }
    function stream_write($data)
    {
        return 0;
    }
    function stream_tell()
    {
        return $this->lob['position'];
    }
    function stream_eof()
    {
        if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
            return true;
        }
        $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
        $result = $db->datatype->_endOfLOB($this->lob);
        if (version_compare(phpversion(), "5.0", ">=")
            && version_compare(phpversion(), "5.1", "<")
        ) {
            return !$result;
        }
        return $result;
    }
    function stream_seek($offset, $whence)
    {
        return false;
    }
    function stream_close()
    {
        if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
            $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
            if (isset($db->datatype->lobs[$this->lob_index])) {
                $db->datatype->_destroyLOB($this->lob_index);
                unset($db->datatype->lobs[$this->lob_index]);
            }
        }
    }
}
if (!stream_wrapper_register("MDB2LOB", "MDB2_LOB")) {
    MDB2::raiseError();
    return false;
}
?>
program/lib/MDB2/Wrapper/peardb.php
New file
@@ -0,0 +1,750 @@
<?php
// +----------------------------------------------------------------------+
// | PHP versions 4 and 5                                                 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
// | Stig. S. Bakken, Lukas Smith                                         |
// | All rights reserved.                                                 |
// +----------------------------------------------------------------------+
// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
// | API as well as database abstraction for PHP applications.            |
// | This LICENSE is in the BSD license style.                            |
// |                                                                      |
// | Redistribution and use in source and binary forms, with or without   |
// | modification, are permitted provided that the following conditions   |
// | are met:                                                             |
// |                                                                      |
// | Redistributions of source code must retain the above copyright       |
// | notice, this list of conditions and the following disclaimer.        |
// |                                                                      |
// | Redistributions in binary form must reproduce the above copyright    |
// | notice, this list of conditions and the following disclaimer in the  |
// | documentation and/or other materials provided with the distribution. |
// |                                                                      |
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
// | Lukas Smith nor the names of his contributors may be used to endorse |
// | or promote products derived from this software without specific prior|
// | written permission.                                                  |
// |                                                                      |
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
// | POSSIBILITY OF SUCH DAMAGE.                                          |
// +----------------------------------------------------------------------+
// | Author: Lukas Smith <smith@pooteeweet.org>                           |
// +----------------------------------------------------------------------+
//
// $Id$
//
/**
 * Wrapper that makes MDB2 behave like PEAR DB
 * WARNING: this wrapper is broken and unmaintained
 *
 * @package MDB2
 * @category Database
 * @author  Lukas Smith <smith@pooteeweet.org>
 */
require_once 'MDB2.php';
/*
 * The method mapErrorCode in each MDB2_dbtype implementation maps
 * native error codes to one of these.
 *
 * If you add an error code here, make sure you also add a textual
 * version of it in DB::errorMessage().
 */
define('DB_OK',                         MDB2_OK);
define('DB_ERROR',                      MDB2_ERROR);
define('DB_ERROR_SYNTAX',               MDB2_ERROR_SYNTAX);
define('DB_ERROR_CONSTRAINT',           MDB2_ERROR_CONSTRAINT);
define('DB_ERROR_NOT_FOUND',            MDB2_ERROR_NOT_FOUND);
define('DB_ERROR_ALREADY_EXISTS',       MDB2_ERROR_ALREADY_EXISTS);
define('DB_ERROR_UNSUPPORTED',          MDB2_ERROR_UNSUPPORTED);
define('DB_ERROR_MISMATCH',             MDB2_ERROR_MISMATCH);
define('DB_ERROR_INVALID',              MDB2_ERROR_INVALID);
define('DB_ERROR_NOT_CAPABLE',          MDB2_ERROR_NOT_CAPABLE);
define('DB_ERROR_TRUNCATED',            MDB2_ERROR_TRUNCATED);
define('DB_ERROR_INVALID_NUMBER',       MDB2_ERROR_INVALID_NUMBER);
define('DB_ERROR_INVALID_DATE',         MDB2_ERROR_INVALID_DATE);
define('DB_ERROR_DIVZERO',              MDB2_ERROR_DIVZERO);
define('DB_ERROR_NODBSELECTED',         MDB2_ERROR_NODBSELECTED);
define('DB_ERROR_CANNOT_CREATE',        MDB2_ERROR_CANNOT_CREATE);
define('DB_ERROR_CANNOT_DROP',          MDB2_ERROR_CANNOT_DROP);
define('DB_ERROR_NOSUCHTABLE',          MDB2_ERROR_NOSUCHTABLE);
define('DB_ERROR_NOSUCHFIELD',          MDB2_ERROR_NOSUCHFIELD);
define('DB_ERROR_NEED_MORE_DATA',       MDB2_ERROR_NEED_MORE_DATA);
define('DB_ERROR_NOT_LOCKED',           MDB2_ERROR_NOT_LOCKED);
define('DB_ERROR_VALUE_COUNT_ON_ROW',   MDB2_ERROR_VALUE_COUNT_ON_ROW);
define('DB_ERROR_INVALID_DSN',          MDB2_ERROR_INVALID_DSN);
define('DB_ERROR_CONNECT_FAILED',       MDB2_ERROR_CONNECT_FAILED);
define('DB_ERROR_EXTENSION_NOT_FOUND',  MDB2_ERROR_EXTENSION_NOT_FOUND);
define('DB_ERROR_ACCESS_VIOLATION',     MDB2_ERROR_ACCESS_VIOLATION);
define('DB_ERROR_NOSUCHDB',             MDB2_ERROR_NOSUCHDB);
define('DB_WARNING',           -1000);
define('DB_WARNING_READ_ONLY', -1001);
define('DB_PARAM_SCALAR',   1);
define('DB_PARAM_OPAQUE',   2);
define('DB_PARAM_MISC',     3);
define('DB_BINMODE_PASSTHRU',   1);
define('DB_BINMODE_RETURN',     2);
define('DB_BINMODE_CONVERT',    3);
define('DB_FETCHMODE_DEFAULT',      MDB2_FETCHMODE_DEFAULT);
define('DB_FETCHMODE_ORDERED',      MDB2_FETCHMODE_ORDERED);
define('DB_FETCHMODE_ASSOC',        MDB2_FETCHMODE_ASSOC);
define('DB_FETCHMODE_OBJECT',       MDB2_FETCHMODE_OBJECT);
define('DB_FETCHMODE_FLIPPED',      MDB2_FETCHMODE_FLIPPED);
define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED);
define('DB_GETMODE_ASSOC',   DB_FETCHMODE_ASSOC);
define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED);
require_once 'MDB2/Extended.php';
define('DB_AUTOQUERY_INSERT', MDB2_AUTOQUERY_INSERT);
define('DB_AUTOQUERY_UPDATE', MDB2_AUTOQUERY_UPDATE);
require_once 'MDB2/Driver/Reverse/Common.php';
define('DB_TABLEINFO_ORDER',        MDB2_TABLEINFO_ORDER);
define('DB_TABLEINFO_ORDERTABLE',   MDB2_TABLEINFO_ORDERTABLE);
define('DB_TABLEINFO_FULL',         MDB2_TABLEINFO_FULL);
define('DB_PORTABILITY_NONE', MDB2_PORTABILITY_NONE);
define('DB_PORTABILITY_LOWERCASE', MDB2_PORTABILITY_FIX_CASE);
define('DB_PORTABILITY_RTRIM', MDB2_PORTABILITY_RTRIM);
define('DB_PORTABILITY_DELETE_COUNT', MDB2_PORTABILITY_DELETE_COUNT);
define('DB_PORTABILITY_NUMROWS', MDB2_PORTABILITY_NUMROWS);
define('DB_PORTABILITY_ERRORS', MDB2_PORTABILITY_ERRORS);
define('DB_PORTABILITY_NULL_TO_EMPTY', MDB2_PORTABILITY_EMPTY_TO_NULL);
define('DB_PORTABILITY_ALL', MDB2_PORTABILITY_ALL);
/**
 * Wrapper that makes MDB2 behave like PEAR DB
 *
 * @package MDB2
 * @category Database
 * @author  Lukas Smith <smith@pooteeweet.org>
 */
class DB
{
    function &factory($type)
    {
        $db =& MDB2::factory($type);
        if (PEAR::isError($db)) {
            return $db;
        }
        $obj =& new MDB2_PEARProxy($db);
        return $obj;
    }
    function &connect($dsn, $options = false)
    {
        if (!is_array($options) && $options) {
            $options = array('persistent' => true);
        }
        $db =& MDB2::connect($dsn, $options);
        if (PEAR::isError($db)) {
            return $db;
        }
        $obj =& new MDB2_PEARProxy($db);
        return $obj;
    }
    function apiVersion()
    {
        return 2;
    }
    function isError($value)
    {
        return PEAR::isError($value);
    }
    function isManip($query)
    {
        return MDB2::isManip($query);
    }
    function errorMessage($value)
    {
        return MDB2::errorMessage($value);
    }
    function parseDSN($dsn)
    {
        return MDB2::parseDSN($dsn);
    }
    function assertExtension($name)
    {
        if (!extension_loaded($name)) {
            $dlext = OS_WINDOWS ? '.dll' : '.so';
            @dl($name . $dlext);
        }
        return extension_loaded($name);
    }
}
/**
 * MDB2_Error implements a class for reporting portable database error
 * messages.
 *
 * @package MDB2
 * @category Database
 * @author  Stig Bakken <ssb@fast.no>
 */
class DB_Error extends PEAR_Error
{
    function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
              $level = E_USER_NOTICE, $debuginfo = null)
    {
        if (is_int($code)) {
            $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, $mode, $level, $debuginfo);
        } else {
            $this->PEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo);
        }
    }
}
/**
 * Wrapper that makes MDB2 behave like PEAR DB
 *
 * @package MDB2
 * @category Database
 * @author  Lukas Smith <smith@pooteeweet.org>
 */
class DB_result extends MDB2_Result_Common
{
    var $result;
    var $row_counter = null;
    var $limit_from  = null;
    var $limit_count = null;
    function DB_result($result)
    {
        $this->result = $result;
    }
    function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
    {
        $arr = $this->result->fetchRow($fetchmode, $rownum);
        if ($this->result->mdb->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
            $this->_convertNullArrayValuesToEmpty($arr);
        }
        return $arr;
    }
    function fetchInto(&$arr, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
    {
        $arr = $this->fetchRow($fetchmode, $rownum);
        if ($this->result->mdb->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
            $this->_convertNullArrayValuesToEmpty($arr);
        }
        return DB_OK;
    }
    function _convertNullArrayValuesToEmpty(&$array)
    {
        if (is_array($array)) {
            foreach ($array as $key => $value) {
                if (is_null($value)) {
                    $array[$key] = '';
                }
            }
        }
    }
    function numCols()
    {
        return $this->result->numCols();
    }
    function numRows()
    {
        return $this->result->numRows();
    }
    function nextResult()
    {
        return $this->result->nextResult();
    }
    function free()
    {
        $err = $this->result->free();
        if (PEAR::isError($err)) {
            return $err;
        }
        $this->result = false;
        return true;
    }
    function tableInfo($mode = null)
    {
        $this->result->db->loadModule('Reverse');
        return $this->result->db->reverse->tableInfo($this->result, $mode);
    }
    function getRowCounter()
    {
        return $this->result->rowCount()+1+$this->result->offset;
    }
}
class DB_row
{
    function DB_row(&$arr)
    {
        for (reset($arr); $key = key($arr); next($arr)) {
            $this->$key = &$arr[$key];
        }
    }
}
class MDB2_PEARProxy extends PEAR
{
    var $db_object;
    var $phptype;
    var $connection;
    var $dsn;
    function MDB2_PEARProxy(&$db_object)
    {
        $this->db_object =& $db_object;
        $this->PEAR('DB_Error');
        $this->db_object->setOption('seqcol_name', 'id');
        $this->db_object->setOption('result_wrap_class', 'DB_result');
        $this->phptype = $this->db_object->phptype;
        $this->connection = $this->db_object->connection;
        $this->dsn = $this->db_object->getDSN();
    }
    function connect($dsninfo, $persistent = false)
    {
        $this->options['persistent'] = $presistent;
        return $this->db_object->connect();
    }
    function disconnect()
    {
        return $this->db_object->disconnect();
    }
    function toString()
    {
        return $this->db_object->__toString();
    }
    function quoteString($string)
    {
        $string = $this->quote($string);
        if ($string{0} == "'") {
            return substr($string, 1, -1);
        }
        return $string;
    }
    function quote($string)
    {
        if (is_null($string)) {
            return 'NULL';
        }
        return $this->db_object->quote($string);
    }
    function escapeSimple($str)
    {
        return $this->db_object->escape($str);
    }
    function quoteSmart($in)
    {
        if (is_int($in) || is_double($in)) {
            return $in;
        } elseif (is_bool($in)) {
            return $in ? 1 : 0;
        } elseif (is_null($in)) {
            return 'NULL';
        } else {
            return "'" . $this->escapeSimple($in) . "'";
        }
    }
    function quoteIdentifier($string)
    {
        return $this->db_object->quoteIdentifier($string);
    }
    // map?
    function provides($feature)
    {
        return $this->db_object->support($feature);
    }
    // remove?
    function errorCode($nativecode)
    {
        return $this->db_object->errorCode($nativecode);
    }
    // remove?
    function errorMessage($dbcode)
    {
        return $this->db_object->errorMessage($dbcode);
    }
    // remove?
    function &raiseError($code = MDB2_ERROR, $mode = null, $options = null,
        $userinfo = null, $nativecode = null)
    {
        return $this->db_object->raiseError($code, $mode, $options, $userinfo, $nativecode);
    }
    function setFetchMode($fetchmode, $object_class = 'stdClass')
    {
        return $this->db_object->setFetchMode($fetchmode, $object_class);
    }
    function setOption($option, $value)
    {
        return $this->db_object->setOption($option, $value);
    }
    function getOption($option)
    {
        return $this->db_object->getOption($option);
    }
    function prepare($query)
    {
        // parse for ! and &
        // set types
        return $this->db_object->prepare($query);
    }
    function autoPrepare($table, $table_fields, $mode = MDB2_AUTOQUERY_INSERT, $where = false)
    {
        $this->db_object->loadModule('Extended');
        // types
        return $this->db_object->extended->autoPrepare($table, $table_fields, $mode, $where);
    }
    function &autoExecute($table, $fields_values, $mode, $where)
    {
        $this->db_object->loadModule('Extended');
        // types
        return $this->db_object->extended->autoExecute($table, $fields_values, $mode, $where);
    }
    function buildManipSQL($table, $table_fields, $mode, $where = false)
    {
        $this->db_object->loadModule('Extended');
        return $this->db_object->extended->buildManipSQL($table, $table_fields, $mode, $where);
    }
    function &execute($stmt, $data = false)
    {
        $stmt->bindParamArray($data);
        return $stmt->execute();
    }
    function executeMultiple($stmt, $data)
    {
        $this->db_object->loadModule('Extended');
        return $this->db_object->extended->executeMultiple($stmt, null, $data);
    }
    function &query($query, $params = array()) {
        if (sizeof($params) > 0) {
            $sth = $this->db_object->prepare($query);
            if (PEAR::isError($sth)) {
                return $sth;
            }
            if (!is_array($params)) {
                $params = array($params);
            }
            $stmt->bindParamArray($params);
            return $stmt->execute();
        }
        return $this->db_object->query($query);
    }
    function simpleQuery($query) {
        $result = $this->db_object->query($query);
        if (PEAR::isError($result) || $result === MDB2_OK) {
            return $result;
        } else {
            return $result->result->getResource();
        }
    }
    function limitQuery($query, $from, $count, $params = array())
    {
        $result = $this->db_object->setLimit($count, $from);
        if (PEAR::isError($result)) {
            return $result;
        }
        $result =& $this->query($query, $params);
        return $result;
    }
    function _convertNullArrayValuesToEmpty(&$array)
    {
        if (is_array($array)) {
            foreach ($array as $key => $value) {
                if (is_null($value)) {
                    $array[$key] = '';
                }
            }
        }
    }
    function &getOne($query, $params = array())
    {
        $result = $this->query($query, $params);
        $one = $result->result->fetchOne();
        if (is_null($one)) {
            $one = '';
        }
        return $one;
    }
    function &getRow($query,
                     $params = array(),
                     $fetchmode = MDB2_FETCHMODE_DEFAULT)
    {
        if (!is_array($params)) {
            if (is_array($fetchmode)) {
                if (is_null($params)) {
                    $tmp = DB_FETCHMODE_DEFAULT;
                } else {
                    $tmp = $params;
                }
                $params = $fetchmode;
                $fetchmode = $tmp;
            } elseif (!is_null($params)) {
                $fetchmode = $params;
                $params = array();
            }
        }
        $result =& $this->query($query, $params);
        return $result->result->fetchRow($fetchmode);
    }
    function &getCol($query, $col = 0, $params = array())
    {
        $result =& $this->query($query, $params);
        $col = $result->result->fetchCol($col);
        $this->_convertNullArrayValuesToEmpty($col);
        return $col;
    }
    function &getAssoc($query, $force_array = false, $params = array(),
                       $fetchmode = MDB2_FETCHMODE_ORDERED, $group = false)
    {
        $result =& $this->query($query, $params);
        $all = $result->result->fetchAll($fetchmode, true, $force_array, $group);
        $first = reset($all);
        if (isset($first) && $this->db_object->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
            if (is_array($first)) {
                foreach ($all as $key => $arr) {
                    $this->_convertNullArrayValuesToEmpty($all[$key]);
                }
            } elseif (is_object($first)) {
                foreach ($all as $key => $arr) {
                    $tmp = get_object_vars($all[$key]);
                    if (is_array($tmp)) {
                        $this->_convertNullArrayValuesToEmpty($tmp);
                        foreach ($tmp as $key2 => $column) {
                            $all[$key]->{$key2} = $column;
                        }
                    }
                }
            }
        }
        return $all;
    }
    function &getAll($query,
                     $params = null,
                     $fetchmode = MDB2_FETCHMODE_DEFAULT)
    {
        if (!is_array($params)) {
            if (is_array($fetchmode)) {
                if (is_null($params)) {
                    $tmp = DB_FETCHMODE_DEFAULT;
                } else {
                    $tmp = $params;
                }
                $params = $fetchmode;
                $fetchmode = $tmp;
            } elseif (!is_null($params)) {
                $fetchmode = $params;
                $params = array();
            }
        }
        $result =& $this->query($query, $params);
        $all = $result->result->fetchAll($fetchmode);
        $first = reset($all);
        if (isset($first) && $this->db_object->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
            if (is_array($first)) {
                foreach ($all as $key => $arr) {
                    $this->_convertNullArrayValuesToEmpty($all[$key]);
                }
            } elseif (is_object($first)) {
                foreach ($all as $key => $arr) {
                    $tmp = get_object_vars($all[$key]);
                    if (is_array($tmp)) {
                        $this->_convertNullArrayValuesToEmpty($tmp);
                        foreach ($tmp as $key2 => $column) {
                            $all[$key]->{$key2} = $column;
                        }
                    }
                }
            }
        }
        return $all;
    }
    function autoCommit($onoff = false)
    {
        return $this->db_object->autoCommit($onoff);
    }
    function commit()
    {
        return $this->db_object->commit();
    }
    function rollback()
    {
        return $this->db_object->rollback();
    }
    function affectedRows()
    {
        return $this->db_object->affectedRows();
    }
    // remove?
    function errorNative()
    {
        return $this->db_object->errorNative();
    }
    function nextId($seq_name, $ondemand = true)
    {
        return $this->db_object->nextID($seq_name, $ondemand);
    }
    function createSequence($seq_name)
    {
        $this->db_object->loadModule('Manager');
        return $this->db_object->manager->createSequence($seq_name, 1);
    }
    function dropSequence($seq_name)
    {
        $this->db_object->loadModule('Manager');
        return $this->db_object->manager->dropSequence($seq_name);
    }
    function &_wrapResource($result)
    {
        if (is_resource($result)) {
            $result_class = $this->db_object->getOption('result_buffering')
                ? $this->db_object->getOption('buffered_result_class') : $$this->db_object->getOption('result_class');
            $class_name = sprintf($result_class, $this->db_object->phptype);
            $result =& new $class_name($this->db_object, $result);
        }
        return $result;
    }
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
    {
        $result = $this->_wrapResource($result);
        if (!is_null($rownum)) {
            $result->result->seek($rownum);
        }
        $arr = $result->fetchRow($fetchmode);
    }
    function freePrepared($prepared)
    {
        return $this->db_object->freePrepared($prepared);
    }
    function freeResult($result)
    {
        $result = $this->_wrapResource($result);
        return $result->free();
    }
    function numCols($result)
    {
        $result = $this->_wrapResource($result);
        return $result->numCols();
    }
    function numRows($result)
    {
        $result = $this->_wrapResource($result);
        return $result->numRows();
    }
    function nextResult($result)
    {
        $result = $this->_wrapResource($result);
        return $result->nextResult();
    }
    function tableInfo($result, $mode = null)
    {
        $result = $this->_wrapResource($result);
        if (is_string($result) || MDB2::isResultCommon($result)) {
            $this->db_object->loadModule('Reverse');
            return $this->db_object->reverse->tableInfo($result, $mode);
        }
        return $result->tableInfo($mode);
    }
    function getTables()
    {
        return $this->getListOf('tables');
    }
    function getListOf($type)
    {
        $this->db_object->loadModule('Manager');
        switch ($type) {
        case 'tables':
            return $this->db_object->manager->listTables();
        case 'views':
            return $this->db_object->manager->listViews();
        case 'users':
            return $this->db_object->manager->listUsers();
        case 'functions':
            return $this->db_object->manager->listFunctions();
        case 'databases':
            return $this->db_object->manager->listDatabases();
        default:
            return $this->db_object->raiseError(MDB2_ERROR_UNSUPPORTED);
        }
    }
}
?>