cvs: php4 /pear DB.php /pear/DB mysql.php
| From: | Stig Bakken | Date: | Tue, 05 Dec 2000 07:00:58 +0000 |
| Subject: | cvs: php4 /pear DB.php /pear/DB mysql.php | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-2946@lists.php.net to get a copy of this message | ||
ssb Mon Dec 4 23:00:58 2000 EDT
Modified files:
/php4/pear/DB mysql.php
/php4/pear DB.php
Log:
* MySQL sequence emulation using auto_increment
* fixed indentation
Index: php4/pear/DB/mysql.php diff -u php4/pear/DB/mysql.php:1.30 php4/pear/DB/mysql.php:1.31 --- php4/pear/DB/mysql.php:1.30 Mon Dec 4 09:13:30 2000 +++ php4/pear/DB/mysql.php Mon Dec 4 23:00:57 2000 @@ -28,594 +28,551 @@ // be registered here. // - require_once "DB/common.php"; +require_once "DB/common.php"; - class DB_mysql extends DB_common - { +class DB_mysql extends DB_common +{ // {{{ properties - var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); // }}} // {{{ constructor - /** - * DB_mysql constructor. - * - * @access public - */ - - function DB_mysql() - { - $this->phptype = "mysql"; - $this->dbsyntax = "mysql"; - $this->features = array( - "prepare" => false, - "pconnect" => true, - "transactions" => false - ); - - $this->errorcode_map = array( - 1004 => DB_ERROR_CANNOT_CREATE, - 1005 => DB_ERROR_CANNOT_CREATE, - 1006 => DB_ERROR_CANNOT_CREATE, - 1007 => DB_ERROR_ALREADY_EXISTS, - 1008 => DB_ERROR_CANNOT_DROP, - 1046 => DB_ERROR_NODBSELECTED, - 1146 => DB_ERROR_NOSUCHTABLE, - 1064 => DB_ERROR_SYNTAX, - 1054 => DB_ERROR_NOSUCHFIELD, - 1062 => DB_ERROR_ALREADY_EXISTS, - 1051 => DB_ERROR_NOSUCHTABLE, - 1100 => DB_ERROR_NOT_LOCKED, - 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, - ); - } + /** + * DB_mysql constructor. + * + * @access public + */ + + function DB_mysql() { + $this->phptype = "mysql"; + $this->dbsyntax = "mysql"; + $this->features = array( + "prepare" => false, + "pconnect" => true, + "transactions" => false + ); + + $this->errorcode_map = array( + 1004 => DB_ERROR_CANNOT_CREATE, + 1005 => DB_ERROR_CANNOT_CREATE, + 1006 => DB_ERROR_CANNOT_CREATE, + 1007 => DB_ERROR_ALREADY_EXISTS, + 1008 => DB_ERROR_CANNOT_DROP, + 1046 => DB_ERROR_NODBSELECTED, + 1146 => DB_ERROR_NOSUCHTABLE, + 1064 => DB_ERROR_SYNTAX, + 1054 => DB_ERROR_NOSUCHFIELD, + 1062 => DB_ERROR_ALREADY_EXISTS, + 1051 => DB_ERROR_NOSUCHTABLE, + 1100 => DB_ERROR_NOT_LOCKED, + 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, + ); + } // }}} // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param $persistent (optional) whether the connection should + * be persistent + * @access public + * @return int DB_OK on success, a DB error on failure + */ + + function connect(&$dsn, $persistent = false) { + if (is_array($dsn)) { + $dsninfo = &$dsn; + } else { + $dsninfo = DB::parseDSN($dsn); + } + + if (!$dsninfo || !$dsninfo["phptype"]) { + return $this->raiseError(); // XXX ERRORMSG + } + + $dbhost = $dsninfo["hostspec"] ? $dsninfo["hostspec"] : "localhost"; + $user = $dsninfo["username"]; + $pw = $dsninfo["password"]; + + $connect_function = $persistent ? "mysql_pconnect" : "mysql_connect"; + + if ($dbhost && $user && $pw) { + $conn = $connect_function($dbhost, $user, $pw); + } elseif ($dbhost && $user) { + $conn = $connect_function($dbhost, $user); + } elseif ($dbhost) { + $conn = $connect_function($dbhost); + } else { + $conn = false; + } + + if ($conn == false) { + return $this->raiseError(); // XXX ERRORMSG + } + + if ($dsninfo["database"]) { + if (!mysql_select_db($dsninfo["database"], $conn)) { + return $this->raiseError(); // XXX ERRORMSG + } + } - /** - * Connect to a database and log in as the specified user. - * - * @param $dsn the data source name (see DB::parseDSN for syntax) - * @param $persistent (optional) whether the connection should - * be persistent - * @access public - * @return int DB_OK on success, a DB error on failure - */ - - function connect( &$dsn, $persistent = false ) - { - if( is_array( $dsn ) ) - { - $dsninfo = &$dsn; - } - - else - { - $dsninfo = DB::parseDSN( $dsn ); - } - - if( !$dsninfo || !$dsninfo[ "phptype" ] ) - { - return $this->raiseError(); // XXX ERRORMSG - } - - $dbhost = $dsninfo[ "hostspec" ] ? $dsninfo[ "hostspec" ] : "localhost"; - $user = $dsninfo[ "username" ]; - $pw = $dsninfo[ "password" ]; - - $connect_function = $persistent ? "mysql_pconnect" : "mysql_connect"; - - if( $dbhost && $user && $pw ) - { - $conn = $connect_function( $dbhost, $user, $pw ); - } - - elseif( $dbhost && $user ) - { - $conn = $connect_function( $dbhost, $user ); - } - - elseif( $dbhost ) - { - $conn = $connect_function( $dbhost ); - } - - else - { - $conn = false; - } - - if( $conn == false ) - { - return $this->raiseError(); // XXX ERRORMSG - } - - if( $dsninfo[ "database" ] ) - { - if( !mysql_select_db( $dsninfo[ "database" ], $conn ) ) - { - return $this->raiseError(); // XXX ERRORMSG - } - } - - $this->connection = $conn; - return DB_OK; - } + $this->connection = $conn; + return DB_OK; + } // }}} // {{{ disconnect() - /** - * Log out and disconnect from the database. - * - * @access public - * - * @return bool TRUE on success, FALSE if not connected. - */ - - function disconnect() - { - return mysql_close( $this->connection ); // XXX ERRORMSG - } + /** + * Log out and disconnect from the database. + * + * @access public + * + * @return bool TRUE on success, FALSE if not connected. + */ + function disconnect() { + return mysql_close($this->connection); + } // }}} // {{{ query() - - /** - * Send a query to MySQL and return the results as a DB_result object. - * - * @param $query the SQL query - * - * @access public - * - * @return object a DB_result object on success, a DB error - * on failure - */ - - function &query( $query ) - { - $this->last_query = $query; - - $result = @mysql_query( $query, $this->connection ); - - if( !$result ) - { - return $this->raiseError( $this->errorCode( mysql_errno( $this->connection ) ) ); - } - - // Determine which queries that should return data, and which - // should return an error code only. - if( preg_match( "/(SELECT|SHOW)/i", $query ) ) - { - return new DB_result( $this, $result ); - } - - else - { - return DB_OK; - } - } + /** + * Send a query to MySQL and return the results as a DB_result object. + * + * @param $query the SQL query + * + * @access public + * + * @return object a DB_result object on success, a DB error + * on failure + */ + function &query($query) { + $this->last_query = $query; + + $result = @mysql_query( $query, $this->connection ); + + if (!$result) { + return $this->raiseError($this->errorCode(mysql_errno($this->connection))); + } + + // Determine which queries that should return data, and which + // should return an error code only. + if (preg_match("/(SELECT|SHOW)/i", $query)) { + return new DB_result($this, $result); + } else { + return DB_OK; + } + } - // }}} + // }}} // {{{ simpleQuery() - /** - * Send a query to MySQL and return the results as a MySQL resource - * identifier. - * - * @param $query the SQL query - * - * @access public - * - * @return int returns a valid MySQL result for successful SELECT - * queries, DB_OK for other successful queries. A DB error is - * returned on failure. - */ - - function simpleQuery( $query ) - { - $this->last_query = $query; - $result = mysql_query( $query, $this->connection ); - - if( !$result ) - { - return $this->raiseError( $this->errorCode( mysql_errno( $this->connection ) ) ); - } - - // Determine which queries that should return data, and which - // should return an error code only. - if( preg_match( "/(SELECT|SHOW)/i", $query ) ) - { - return $result; - } - - else - { - return DB_OK; - } - } + /** + * Send a query to MySQL and return the results as a MySQL resource + * identifier. + * + * @param $query the SQL query + * + * @access public + * + * @return int returns a valid MySQL result for successful SELECT + * queries, DB_OK for other successful queries. A DB error is + * returned on failure. + */ + function simpleQuery($query) { + $this->last_query = $query; + $result = mysql_query( $query, $this->connection ); + + if (!$result) { + return $this->raiseError($this->errorCode(mysql_errno($this->connection))); + } + + // Determine which queries that should return data, and which + // should return an error code only. + if (preg_match("/(SELECT|SHOW)/i", $query)) { + return $result; + } else { + return DB_OK; + } + } - // }}} + // }}} // {{{ fetchRow() - /** - * Fetch a row and return as array. - * - * @param $result MySQL result identifier - * @param $fetchmode how the resulting array should be indexed - * - * @access public - * - * @return mixed an array on success, a DB error on failure, NULL - * if there is no more data - */ - - function &fetchRow( $result, $fetchmode = DB_FETCHMODE_DEFAULT ) - { - if( $fetchmode == DB_FETCHMODE_DEFAULT ) - { - $fetchmode = $this->fetchmode; - } - - if( $fetchmode & DB_FETCHMODE_ASSOC ) - { - $row = mysql_fetch_array( $result, MYSQL_ASSOC ); - } - - else - { - $row = mysql_fetch_row( $result ); - } - - if( !$row ) - { - $errno = mysql_errno( $this->connection ); - - if( !$errno ) - { - return NULL; - } - - return $this->raiseError( $this->errorCode( $errno ) ); - } - - return $row; - } + /** + * Fetch a row and return as array. + * + * @param $result MySQL result identifier + * @param $fetchmode how the resulting array should be indexed + * + * @access public + * + * @return mixed an array on success, a DB error on failure, NULL + * if there is no more data + */ + function &fetchRow($result, $fetchmode = DB_FETCHMODE_DEFAULT) { + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $row = mysql_fetch_array($result, MYSQL_ASSOC); + } else { + $row = mysql_fetch_row($result); + } + + if (!$row) { + $errno = mysql_errno($this->connection); + + if (!$errno) { + return NULL; + } + + return $this->raiseError($this->errorCode($errno)); + } + + return $row; + } - // }}} + // }}} // {{{ fetchInto() - /** - * Fetch a row and insert the data into an existing array. - * - * @param $result MySQL result identifier - * @param $arr (reference) array where data from the row is stored - * @param $fetchmode how the array data should be indexed - * - * @access public - * - * @return int DB_OK on success, a DB error on failure - */ - - function fetchInto($result, &$arr, $fetchmode = DB_FETCHMODE_DEFAULT ) - { - if( $fetchmode == DB_FETCHMODE_DEFAULT ) - { - $fetchmode = $this->fetchmode; - } - - if( $fetchmode & DB_FETCHMODE_ASSOC ) - { - $arr = mysql_fetch_array( $result, MYSQL_ASSOC ); - } - - else - { - $arr = mysql_fetch_row( $result ); - } - - if( !$arr ) - { - $errno = mysql_errno( $this->connection ); - - if( !$errno ) - { - return NULL; - } + /** + * Fetch a row and insert the data into an existing array. + * + * @param $result MySQL result identifier + * @param $arr (reference) array where data from the row is stored + * @param $fetchmode how the array data should be indexed + * + * @access public + * + * @return int DB_OK on success, a DB error on failure + */ + function fetchInto($result, &$arr, $fetchmode = DB_FETCHMODE_DEFAULT) { + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = mysql_fetch_array($result, MYSQL_ASSOC); + } else { + $arr = mysql_fetch_row($result); + } + + if (!$arr) { + $errno = mysql_errno($this->connection); + + if (!$errno) { + return NULL; + } - return $this->raiseError( $this->errorCode( $errno ) ); - } + return $this->raiseError($this->errorCode($errno)); + } - return DB_OK; - } + return DB_OK; + } - // }}} + // }}} // {{{ freeResult() - /** - * Free the internal resources associated with $result. - * - * @param $result MySQL result identifier or DB statement identifier - * - * @access public - * - * @return bool TRUE on success, FALSE if $result is invalid - */ - - function freeResult( $result ) - { - if( is_resource( $result ) ) - { - return mysql_free_result( $result ); - } - - if( !isset( $this->prepare_tokens[ $result ] ) ) - { - return false; - } + /** + * Free the internal resources associated with $result. + * + * @param $result MySQL result identifier or DB statement identifier + * + * @access public + * + * @return bool TRUE on success, FALSE if $result is invalid + */ + function freeResult($result) { + if (is_resource($result)) { + return mysql_free_result($result); + } + + if (!isset($this->prepare_tokens[$result])) { + return false; + } - unset( $this->prepare_tokens[ $result ] ); - unset( $this->prepare_types[ $result ] ); + unset($this->prepare_tokens[$result]); + unset($this->prepare_types[$result]); - return true; - } + return true; + } - // }}} + // }}} // {{{ numCols() - /** - * Get the number of columns in a result set. - * - * @param $result MySQL result identifier - * - * @access public - * - * @return int the number of columns per row in $result - */ - - function numCols( $result ) - { - $cols = mysql_num_fields( $result ); - - if( !$cols ) - { - return $this->raiseError( $this->errorCode( mysql_errno( $this->connection ) ) ); - } + /** + * Get the number of columns in a result set. + * + * @param $result MySQL result identifier + * + * @access public + * + * @return int the number of columns per row in $result + */ + function numCols($result) { + $cols = mysql_num_fields($result); + + if (!$cols) { + return $this->raiseError($this->errorCode(mysql_errno($this->connection))); + } - return $cols; - } + return $cols; + } // }}} // {{{ errorNative() + + /** + * Get the native error code of the last error (if any) that + * occured on the current connection. + * + * @access public + * + * @return int native MySQL error code + */ - /** - * Get the native error code of the last error (if any) that - * occured on the current connection. - * - * @access public - * - * @return int native MySQL error code - */ - - function errorNative() - { - return mysql_errno( $this->connection ); - } + function errorNative() { + return mysql_errno($this->connection); + } // }}} // {{{ prepare() - /** - * Prepares a query for multiple execution with execute(). With - * MySQL, this is emulated. - * - * @param $query the SQL query to prepare for execution. The - * characters "?" and "&" have special meanings. "?" is a scalar - * placeholder, the value provided for it when execute()ing is - * inserted as-is. "&" is an opaque placeholder, the value - * provided on execute() is a file name, and the contents of that - * file will be used in the query. - * - * @access public - * - * @return int identifier for this prepared query - */ - - function prepare( $query ) - { - $tokens = split( "[\&\?]", $query ); - $token = 0; - $types = array(); - - for( $i = 0; $i < strlen( $query ); $i++ ) - { - switch( $query[ $i ] ) - { - case "?": $types[ $token++ ] = DB_PARAM_SCALAR; - - break; - case '&': $types[ $token++ ] = DB_PARAM_OPAQUE; - - break; - } - } + /** + * Prepares a query for multiple execution with execute(). With + * MySQL, this is emulated. + * + * @param $query the SQL query to prepare for execution. The + * characters "?" and "&" have special meanings. "?" is a scalar + * placeholder, the value provided for it when execute()ing is + * inserted as-is. "&" is an opaque placeholder, the value + * provided on execute() is a file name, and the contents of that + * file will be used in the query. + * + * @access public + * + * @return int identifier for this prepared query + */ + + function prepare($query) { + $tokens = split("[\&\?]", $query); + $token = 0; + $types = array(); + + for ($i = 0; $i < strlen($query); $i++) { + switch ($query[$i]) { + case "?": + $types[$token++] = DB_PARAM_SCALAR; + break; + case '&': + $types[$token++] = DB_PARAM_OPAQUE; + break; + } + } - $this->prepare_tokens[] = &$tokens; - end( $this->prepare_tokens ); + $this->prepare_tokens[] = &$tokens; + end($this->prepare_tokens); - $k = key( $this->prepare_tokens ); - $this->prepare_types[ $k ] = $types; + $k = key($this->prepare_tokens); + $this->prepare_types[$k] = $types; - return $k; - } + return $k; + } // }}} // {{{ execute() - /** - * Executes a prepared query and substitutes placeholders with - * provided values. - * - * @return int returns a MySQL result resource for successful - * SELECT queries, DB_OK for other successful queries. A DB error - * is returned on failure. - * - * @see DB_mysql::prepare - */ - - function execute( $stmt, $data = false ) - { - $realquery = $this->execute_emulate_query( $stmt, $data ); - $this->last_query = $realquery; - - $result = mysql_query( $realquery, $this->connection ); - - if( !$result ) - { - return $this->raiseError( $this->errorCode( mysql_errno( $this->connection ) ) ); - } - - if( preg_match( "/(SELECT|SHOW)/i", $realquery ) ) - { - return $result; - } - - else - { - return DB_OK; - } - } + /** + * Executes a prepared query and substitutes placeholders with + * provided values. + * + * @return int returns a MySQL result resource for successful + * SELECT queries, DB_OK for other successful queries. A DB error + * is returned on failure. + * + * @see DB_mysql::prepare + */ + function execute($stmt, $data = false) { + $realquery = $this->execute_emulate_query($stmt, $data); + $this->last_query = $realquery; + + $result = mysql_query($realquery, $this->connection); + + if (!$result) { + return $this->raiseError($this->errorCode(mysql_errno($this->connection))); + } + + if (preg_match("/(SELECT|SHOW)/i", $realquery)) { + return $result; + } else { + return DB_OK; + } + } // }}} // {{{ autoCommit() - /** - * Enable/disable automatic commits [not supported by MySQL] - * - * @access public - * - * @return object DB error code (not capable) - */ - - function autoCommit( $onoff = false ) - { - return $this->raiseError( DB_ERROR_NOT_CAPABLE ); - } + /** + * Enable/disable automatic commits [not supported by MySQL] + * + * @access public + * + * @return object DB error code (not capable) + */ + function autoCommit($onoff = false) { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } // }}} // {{{ commit() - /** - * Commit transactions on the current connection [not supported by MySQL] - * - * @access public - * - * @return object DB error code (not capable) - */ - - function commit() - { - return $this->raiseError( DB_ERROR_NOT_CAPABLE ); - } + /** + * Commit transactions on the current connection [not supported by MySQL] + * + * @access public + * + * @return object DB error code (not capable) + */ + function commit() { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } // }}} // {{{ rollback() - /** - * Roll back all uncommitted transactions on the current connection. - * [not supported by MySQL] - * - * @access public - * - * @return object DB error code (not capable) - */ - - function rollback() - { - return $this->raiseError( DB_ERROR_NOT_CAPABLE ); - } + /** + * Roll back all uncommitted transactions on the current connection. + * [not supported by MySQL] + * + * @access public + * + * @return object DB error code (not capable) + */ + function rollback() { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } // }}} // {{{ lock() - function lock( $table, $mode = "write" ) - { - // build query string to lock tables - $query = "lock tables "; - - // parameter $table is an array - parse it - if( is_array( $table ) ) - { - // walk through array - while( list( $key, $value ) = each( $table ) ) - { - if( $key == "read" && $key != 0) - { - $query .= "$value read, "; - } - - else - { - $query .= "$value $mode, "; - } - } - - $query = substr( $query, 0, -2 ); - } - - // parameter $table is just one table name - else - { - $query .= "$table $mode"; - } - - // perform query to lock table(s) - $result = mysql_query( $query, $this->connection ); - - // check, if query succeeded - if( !$result ) - { - // query failed - return false; - } + function lock($table, $mode = "write") { + // build query string to lock tables + $query = "lock tables "; + + // parameter $table is an array - parse it + if (is_array($table)) { + // walk through array + while (list($key, $value) = each($table)) { + if ($key == "read" && $key != 0) { + $query .= "$value read, "; + } else { + $query .= "$value $mode, "; + } + } + + $query = substr($query, 0, -2); + } + // parameter $table is just one table name + else { + $query .= "$table $mode"; + } + + // perform query to lock table(s) + $result = mysql_query($query, $this->connection); + + // check, if query succeeded + if (!$result) { + // query failed + return false; + } - // query succeeded - return true; + // query succeeded + return true; } // }}} - // {{{ lock() + // {{{ unlock() + + function unlock() { + // perform query to unlock tables + $result = mysql_query("unlock tables", $this->connection); + + // check, if query succeeded + if (!$result) { + // query failed + return false; + } + + // query succeeded + return true; + } - function unlock() - { - // perform query to unlock tables - $result = mysql_query( "unlock tables", $this->connection ); + // }}} + // {{{ nextId() - // check, if query succeeded - if( !$result ) - { - // query failed - return false; - } + /** + * Get the next value in a sequence. We emulate sequences + * for MySQL. Will create the sequence if it does not exist. + * + * @access public + * + * @param $seq_name the name of the sequence + * + * @param $ondemand whether to create the sequence table on demand + * (default is true) + * + * @return a sequence integer, or a DB error + */ + function nextId($seq_name, $ondemand = true) { + $sqn = preg_replace('/[^a-z0-9_]/i', '_', $seq_name); + $repeat = 0; + do { + $result = $this->query("INSERT INTO db_seq_${sqn} VALUES(NULL)"); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $result; + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $result; + } + return mysql_insert_id($this->connection); + } + + // }}} + // {{{ createSequence() - // query succeeded - return true; + function createSequence($seq_name) { + $sqn = preg_replace('/[^a-z0-9_]/i', '_', $seq_name); + return $this->query("CREATE TABLE db_seq_${sqn} ". + "(id INTEGER AUTO_INCREMENT, PRIMARY KEY(id))"); } // }}} - // TODO/wishlist: - // simpleFetch - // simpleGet - // affectedRows + // TODO/wishlist: + // simpleFetch + // simpleGet + // affectedRows // longReadlen // binmode - } +} + ?> Index: php4/pear/DB.php diff -u php4/pear/DB.php:1.37 php4/pear/DB.php:1.38 --- php4/pear/DB.php:1.37 Mon Dec 4 09:13:29 2000 +++ php4/pear/DB.php Mon Dec 4 23:00:57 2000 @@ -17,150 +17,147 @@ // | | // +----------------------------------------------------------------------+ // -// $Id: DB.php,v 1.37 2000/12/04 17:13:29 sbergmann Exp $ +// $Id: DB.php,v 1.38 2000/12/05 07:00:57 ssb Exp $ // // Database independent query interface. // - require_once "PEAR.php"; - require_once "DB/ERROR.php"; - require_once "DB/RESULT.php"; - require_once "DB/WARNING.php"; - - /* - * The method mapErrorCode in each DB_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", 0 ); - define( "DB_ERROR", -1 ); - define( "DB_ERROR_SYNTAX", -2 ); - define( "DB_ERROR_CONSTRAINT", -3 ); - define( "DB_ERROR_NOT_FOUND", -4 ); - define( "DB_ERROR_ALREADY_EXISTS", -5 ); - define( "DB_ERROR_UNSUPPORTED", -6 ); - define( "DB_ERROR_MISMATCH", -7 ); - define( "DB_ERROR_INVALID", -8 ); - define( "DB_ERROR_NOT_CAPABLE", -9 ); - define( "DB_ERROR_TRUNCATED", -10 ); - define( "DB_ERROR_INVALID_NUMBER", -11 ); - define( "DB_ERROR_INVALID_DATE", -12 ); - define( "DB_ERROR_DIVZERO", -13 ); - define( "DB_ERROR_NODBSELECTED", -14 ); - define( "DB_ERROR_CANNOT_CREATE", -15 ); - define( "DB_ERROR_CANNOT_DELETE", -16 ); - define( "DB_ERROR_CANNOT_DROP", -17 ); - define( "DB_ERROR_NOSUCHTABLE", -18 ); - define( "DB_ERROR_NOSUCHFIELD", -19 ); - define( "DB_ERROR_NEED_MORE_DATA", -20 ); - define( "DB_ERROR_NOT_LOCKED", -21 ); - define( "DB_ERROR_VALUE_COUNT_ON_ROW", -22 ); - - /* - * Warnings are not detected as errors by DB::isError(), and are not - * fatal. You can detect whether an error is in fact a warning with - * DB::isWarning(). - */ - - define( "DB_WARNING", -1000 ); - define( "DB_WARNING_READ_ONLY", -1001 ); - - /* - * These constants are used when storing information about prepared - * statements (using the "prepare" method in DB_dbtype). - * - * The prepare/execute model in DB is mostly borrowed from the ODBC - * extension, in a query the "?" character means a scalar parameter. - * There is one extension though, a "*" character means an opaque - * parameter. An opaque parameter is simply a file name, the real - * data are in that file (useful for stuff like putting uploaded files - * into your database). - */ - - define( "DB_PARAM_SCALAR", 1 ); - define( "DB_PARAM_OPAQUE", 2 ); - - /* - * These constants define different ways of returning binary data - * from queries. Again, this model has been borrowed from the ODBC - * extension. - * - * DB_BINMODE_PASSTHRU sends the data directly through to the browser - * when data is fetched from the database. - * DB_BINMODE_RETURN lets you return data as usual. - * DB_BINMODE_CONVERT returns data as well, only it is converted to - * hex format, for example the string "123" would become "313233". - */ - - define( "DB_BINMODE_PASSTHRU", 1 ); - define( "DB_BINMODE_RETURN", 2 ); - define( "DB_BINMODE_CONVERT", 3 ); - - /** - * This is a special constant that tells DB the user hasn't specified - * any particular get mode, so the default should be used. - */ - - define( "DB_FETCHMODE_DEFAULT", 0 ); - - /** - * Column data indexed by numbers, ordered from 0 and up - */ - - define( "DB_FETCHMODE_ORDERED", 1 ); - - /** - * Column data indexed by column names - */ - - define( "DB_FETCHMODE_ASSOC", 2 ); - - /** - * For multi-dimensional results: normally the first level of arrays - * is the row number, and the second level indexed by column number or name. - * DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays - * is the column name, and the second level the row number. - */ - - define( "DB_FETCHMODE_FLIPPED", 4 ); - - /* for compatibility */ - - define( "DB_GETMODE_ORDERED", DB_FETCHMODE_ORDERED ); - define( "DB_GETMODE_ASSOC", DB_FETCHMODE_ASSOC ); - define( "DB_GETMODE_FLIPPED", DB_FETCHMODE_FLIPPED ); - - /** - * The main "DB" class is simply a container class with some static - * methods for creating DB objects as well as some utility functions - * common to all parts of DB. - * - * The object model of DB is as follows (indentation means inheritance): - * - * DB The main DB class. This is simply a utility class - * with some "static" methods for creating DB objects as - * well as common utility functions for other DB classes. - * - * DB_common The base for each DB implementation. Provides default - * | implementations (in OO lingo virtual methods) for - * | the actual DB implementations as well as a bunch of - * | query utility functions. - * | - * +-DB_mysql The DB implementation for MySQL. Inherits DB_common. - * When calling DB::factory or DB::connect for MySQL - * connections, the object returned is an instance of this - * class. - * - * @version 2 - * @author Stig Bakken <ssb@fast.no> - * @since PHP 4.0 - */ +require_once "PEAR.php"; - class DB - { +/* + * The method mapErrorCode in each DB_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", 0); +define("DB_ERROR", -1); +define("DB_ERROR_SYNTAX", -2); +define("DB_ERROR_CONSTRAINT", -3); +define("DB_ERROR_NOT_FOUND", -4); +define("DB_ERROR_ALREADY_EXISTS", -5); +define("DB_ERROR_UNSUPPORTED", -6); +define("DB_ERROR_MISMATCH", -7); +define("DB_ERROR_INVALID", -8); +define("DB_ERROR_NOT_CAPABLE", -9); +define("DB_ERROR_TRUNCATED", -10); +define("DB_ERROR_INVALID_NUMBER", -11); +define("DB_ERROR_INVALID_DATE", -12); +define("DB_ERROR_DIVZERO", -13); +define("DB_ERROR_NODBSELECTED", -14); +define("DB_ERROR_CANNOT_CREATE", -15); +define("DB_ERROR_CANNOT_DELETE", -16); +define("DB_ERROR_CANNOT_DROP", -17); +define("DB_ERROR_NOSUCHTABLE", -18); +define("DB_ERROR_NOSUCHFIELD", -19); +define("DB_ERROR_NEED_MORE_DATA", -20); +define("DB_ERROR_NOT_LOCKED", -21); +define("DB_ERROR_VALUE_COUNT_ON_ROW", -22); + +/* + * Warnings are not detected as errors by DB::isError(), and are not + * fatal. You can detect whether an error is in fact a warning with + * DB::isWarning(). + */ + +define("DB_WARNING", -1000); +define("DB_WARNING_READ_ONLY", -1001); + +/* + * These constants are used when storing information about prepared + * statements (using the "prepare" method in DB_dbtype). + * + * The prepare/execute model in DB is mostly borrowed from the ODBC + * extension, in a query the "?" character means a scalar parameter. + * There is one extension though, a "*" character means an opaque + * parameter. An opaque parameter is simply a file name, the real + * data are in that file (useful for stuff like putting uploaded files + * into your database). + */ + +define("DB_PARAM_SCALAR", 1); +define("DB_PARAM_OPAQUE", 2); + +/* + * These constants define different ways of returning binary data + * from queries. Again, this model has been borrowed from the ODBC + * extension. + * + * DB_BINMODE_PASSTHRU sends the data directly through to the browser + * when data is fetched from the database. + * DB_BINMODE_RETURN lets you return data as usual. + * DB_BINMODE_CONVERT returns data as well, only it is converted to + * hex format, for example the string "123" would become "313233". + */ + +define("DB_BINMODE_PASSTHRU", 1); +define("DB_BINMODE_RETURN", 2); +define("DB_BINMODE_CONVERT", 3); + +/** + * This is a special constant that tells DB the user hasn't specified + * any particular get mode, so the default should be used. + */ + +define("DB_FETCHMODE_DEFAULT", 0); + +/** + * Column data indexed by numbers, ordered from 0 and up + */ + +define("DB_FETCHMODE_ORDERED", 1); + +/** + * Column data indexed by column names + */ + +define("DB_FETCHMODE_ASSOC", 2); + +/** + * For multi-dimensional results: normally the first level of arrays + * is the row number, and the second level indexed by column number or name. + * DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays + * is the column name, and the second level the row number. + */ + +define("DB_FETCHMODE_FLIPPED", 4); + +/* for compatibility */ + +define("DB_GETMODE_ORDERED", DB_FETCHMODE_ORDERED); +define("DB_GETMODE_ASSOC", DB_FETCHMODE_ASSOC); +define("DB_GETMODE_FLIPPED", DB_FETCHMODE_FLIPPED); + +/** + * The main "DB" class is simply a container class with some static + * methods for creating DB objects as well as some utility functions + * common to all parts of DB. + * + * The object model of DB is as follows (indentation means inheritance): + * + * DB The main DB class. This is simply a utility class + * with some "static" methods for creating DB objects as + * well as common utility functions for other DB classes. + * + * DB_common The base for each DB implementation. Provides default + * | implementations (in OO lingo virtual methods) for + * | the actual DB implementations as well as a bunch of + * | query utility functions. + * | + * +-DB_mysql The DB implementation for MySQL. Inherits DB_common. + * When calling DB::factory or DB::connect for MySQL + * connections, the object returned is an instance of this + * class. + * + * @version 2 + * @author Stig Bakken <ssb@fast.no> + * @since PHP 4.0 + */ + +class DB +{ /** * Create a new DB object for the specified database type * @@ -170,26 +167,26 @@ * error */ - function &factory( $type ) + function &factory($type) { - @include_once( "DB/${type}.php" ); + @include_once("DB/${type}.php"); - $classname = "DB_" . $type; - $obj = @new $classname; + $classname = "DB_" . $type; + $obj = @new $classname; - if( !$obj ) - { - return new DB_Error( DB_ERROR_NOT_FOUND ); - } + if (!$obj) { + return new DB_Error(DB_ERROR_NOT_FOUND); + } - return $obj; + return $obj; } /** * Create a new DB object and connect to the specified database * - * @param $dsn string "data source name", see the DB::parseDSN - * method for a description of the dsn format. + * @param $dsn mixed "data source name", see the DB::parseDSN + * method for a description of the dsn format. Can also be + * specified as an array of the format returned by DB::parseDSN. * * @param $persistent bool whether this connection should be * persistent. Ignored if the backend extension does not support @@ -197,30 +194,33 @@ * * @return object a newly created DB object, or a DB error code on * error + * + * @see DB::parseDSN */ - - function &connect( $dsn, $persistent = false ) + function &connect($dsn, $persistent = false) { - $dsninfo = DB::parseDSN( $dsn ); - $type = $dsninfo[ "phptype" ]; - $classname = "DB_" . $type; - - @include_once( "DB/${type}.php" ); - $obj = @new $classname; - - if( !$obj ) - { - return new DB_Error( DB_ERROR_NOT_FOUND ); - } + if (is_array($dsn)) { + $dsninfo = $dsn; + } else { + $dsninfo = DB::parseDSN($dsn); + } + $type = $dsninfo["phptype"]; + $classname = "DB_" . $type; + + @include_once("DB/${type}.php"); + $obj = @new $classname; + + if (!$obj) { + return new DB_Error(DB_ERROR_NOT_FOUND); + } + + $err = $obj->connect($dsninfo, $persistent); + + if (DB::isError($err)) { + return $err; + } - $err = $obj->connect( $dsninfo, $persistent ); - - if( DB::isError( $err ) ) - { - return $err; - } - - return $obj; + return $obj; } /** @@ -228,10 +228,9 @@ * * @return int the DB API version number */ - function apiVersion() { - return 2; + return 2; } /** @@ -241,13 +240,11 @@ * * @return bool whether $value is an error */ - - function isError( $value ) + function isError($value) { - return is_object( $value ) && - ( get_class( $value ) == "db_error" || - is_subclass_of( $value, "db_error" ) - ); + return is_object($value) && + (get_class($value) == "db_error" || + is_subclass_of($value, "db_error")); } /** @@ -259,13 +256,11 @@ * * @return bool whether $value is a warning */ - - function isWarning( $value ) + function isWarning($value) { - return is_object( $value ) && - ( get_class( $value ) == "db_warning" || - is_subclass_of( $value, "db_warning" ) - ); + return is_object($value) && + (get_class( $value ) == "db_warning" || + is_subclass_of($value, "db_warning")); } /** @@ -276,44 +271,41 @@ * @return string error message, or false if the error code was * not recognized */ - - function errorMessage( $value ) + function errorMessage($value) { - if( !isset( $errorMessages ) ) - { - $errorMessages = array( - DB_ERROR => "unknown error", - DB_ERROR_ALREADY_EXISTS => "already exists", - DB_ERROR_CANNOT_CREATE => "can not create", - DB_ERROR_CANNOT_DELETE => "can not delete", - DB_ERROR_CANNOT_DROP => "can not drop", - DB_ERROR_CONSTRAINT => "constraint violation", - DB_ERROR_DIVZERO => "division by zero", - DB_ERROR_INVALID => "invalid", - DB_ERROR_INVALID_DATE => "invalid date or time", - DB_ERROR_INVALID_NUMBER => "invalid number", - DB_ERROR_MISMATCH => "mismatch", - DB_ERROR_NODBSELECTED => "no database selected", - DB_ERROR_NOSUCHFIELD => "no such field", - DB_ERROR_NOSUCHTABLE => "no such table", - DB_ERROR_NOT_CAPABLE => "DB backend not capable", - DB_ERROR_NOT_FOUND => "not found", - DB_ERROR_NOT_LOCKED => "not locked", - DB_ERROR_SYNTAX => "syntax error", - DB_ERROR_UNSUPPORTED => "not supported", - DB_ERROR_VALUE_COUNT_ON_ROW => "value count on row", - DB_OK => "no error", - DB_WARNING => "unknown warning", - DB_WARNING_READ_ONLY => "read only" - ); - } + if (!isset($errorMessages)) { + $errorMessages = array( + DB_ERROR => "unknown error", + DB_ERROR_ALREADY_EXISTS => "already exists", + DB_ERROR_CANNOT_CREATE => "can not create", + DB_ERROR_CANNOT_DELETE => "can not delete", + DB_ERROR_CANNOT_DROP => "can not drop", + DB_ERROR_CONSTRAINT => "constraint violation", + DB_ERROR_DIVZERO => "division by zero", + DB_ERROR_INVALID => "invalid", + DB_ERROR_INVALID_DATE => "invalid date or time", + DB_ERROR_INVALID_NUMBER => "invalid number", + DB_ERROR_MISMATCH => "mismatch", + DB_ERROR_NODBSELECTED => "no database selected", + DB_ERROR_NOSUCHFIELD => "no such field", + DB_ERROR_NOSUCHTABLE => "no such table", + DB_ERROR_NOT_CAPABLE => "DB backend not capable", + DB_ERROR_NOT_FOUND => "not found", + DB_ERROR_NOT_LOCKED => "not locked", + DB_ERROR_SYNTAX => "syntax error", + DB_ERROR_UNSUPPORTED => "not supported", + DB_ERROR_VALUE_COUNT_ON_ROW => "value count on row", + DB_OK => "no error", + DB_WARNING => "unknown warning", + DB_WARNING_READ_ONLY => "read only" + ); + } + + if (DB::isError($value)) { + $value = $value->code; + } - if( DB::isError( $value ) ) - { - $value = $value->code; - } - - return $errorMessages[ $value ]; + return $errorMessages[$value]; } /** @@ -322,135 +314,233 @@ * @param $dsn string Data Source Name to be parsed * * @return array an associative array with the following keys: - * <dl> - * <dt>phptype</dt> - * <dd>Database backend used in PHP (mysql, odbc etc.)</dd> - * <dt>dbsyntax</dt> - * <dd>Database used with regards to SQL syntax etc.</dd> - * <dt>protocol</dt> - * <dd>Communication protocol to use (tcp, unix etc.)</dd> - * <dt>hostspec</dt> - * <dd>Host specification (hostname[:port])</dd> - * <dt>database</dt> - * <dd>Database to use on the DBMS server</dd> - * <dt>username</dt> - * <dd>User name for login</dd> - * <dt>password</dt> - * <dd>Password for login</dd> - * </dl> - * </p> * - * <p> + * phptype: Database backend used in PHP (mysql, odbc etc.) + * dbsyntax: Database used with regards to SQL syntax etc. + * protocol: Communication protocol to use (tcp, unix etc.) + * hostspec: Host specification (hostname[:port]) + * database: Database to use on the DBMS server + * username: User name for login + * password: Password for login + * * The format of the supplied DSN is in its fullest form: - * <ul> - * <li>phptype(dbsyntax)://username:password@protocol+hostspec/database</li> - * </ul> + * + * phptype(dbsyntax)://username:password@protocol+hostspec/database + * * Most variations are allowed: - * <ul> - * <li>phptype://username:password@protocol+hostspec/database</li> - * <li>phptype://username:password@hostspec/database</li> - * <li>phptype://username:password@hostspec</li> - * <li>phptype://hostspec/database</li> - * <li>phptype://hostspec</li> - * <li>phptype(dbsyntax)</li> - * <li>phptype</li> - * </ul> - * </p> + * phptype://username:password@protocol+hostspec/database</li> + * phptype://username:password@hostspec/database</li> + * phptype://username:password@hostspec</li> + * phptype://hostspec/database</li> + * phptype://hostspec</li> + * phptype(dbsyntax)</li> + * phptype</li> * * @return bool FALSE is returned on error */ - - function parseDSN( $dsn ) + function parseDSN($dsn) { - if( is_array( $dsn ) ) - { - return $dsn; - } + if (is_array($dsn)) { + return $dsn; + } - $parsed = array( - "phptype" => false, - "dbsyntax" => false, - "protocol" => false, - "hostspec" => false, - "database" => false, - "username" => false, - "password" => false - ); - - if( preg_match( "|^([^:]+)://|", $dsn, $arr ) ) - { - $dbtype = $arr[ 1 ]; - $dsn = preg_replace( "|^[^:]+://|", '', $dsn ); - - // match "phptype(dbsyntax)" - if( preg_match( "|^([^\(]+)\((.+)\)$|", $dbtype, $arr ) ) - { - $parsed[ "phptype" ] = $arr[ 1 ]; - $parsed[ "dbsyntax" ] = $arr[ 2 ]; - } + $parsed = array( + "phptype" => false, + "dbsyntax" => false, + "protocol" => false, + "hostspec" => false, + "database" => false, + "username" => false, + "password" => false + ); + + if (preg_match("|^([^:]+)://|", $dsn, $arr)) { + $dbtype = $arr[ 1 ]; + $dsn = preg_replace( "|^[^:]+://|", '', $dsn); + + // match "phptype(dbsyntax)" + if (preg_match("|^([^\(]+)\((.+)\)$|", $dbtype, $arr)) { + $parsed["phptype"] = $arr[1]; + $parsed["dbsyntax"] = $arr[2]; + } else { + $parsed["phptype"] = $dbtype; + } + } else { + // match "phptype(dbsyntax)" + if (preg_match("|^([^\(]+)\((.+)\)$|", $dsn, $arr)) { + $parsed["phptype"] = $arr[1]; + $parsed["dbsyntax"] = $arr[2]; + } else { + $parsed["phptype"] = $dsn; + } - else - { - $parsed[ "phptype" ] = $dbtype; + return $parsed; } + if (preg_match("|^(.*)/([^/]+)/?$|", $dsn, $arr)) { + $parsed["database"] = $arr[2]; + $dsn = $arr[1]; } - else - { - // match "phptype(dbsyntax)" - if( preg_match( "|^([^\(]+)\((.+)\)$|", $dsn, $arr ) ) - { - $parsed[ "phptype" ] = $arr[ 1 ]; - $parsed[ "dbsyntax" ] = $arr[ 2 ]; - } - - else - { - $parsed[ "phptype" ] = $dsn; - } - - return $parsed; + if (preg_match("|^([^:]+):([^@]+)@?(.*)$|", $dsn, $arr)) { + $parsed["username"] = $arr[1]; + $parsed["password"] = $arr[2]; + $dsn = $arr[3]; + } elseif (preg_match("|^([^:]+)@(.*)$|", $dsn, $arr)) { + $parsed["username"] = $arr[1]; + $dsn = $arr[2]; } - if( preg_match( "|^(.*)/([^/]+)/?$|", $dsn, $arr ) ) - { - $parsed[ "database" ] = $arr[ 2 ]; - $dsn = $arr[ 1 ]; + if (preg_match("|^([^\+]+)\+(.*)$|", $dsn, $arr)) { + $parsed["protocol"] = $arr[1]; + $dsn = $arr[2]; } - if( preg_match( "|^([^:]+):([^@]+)@?(.*)$|", $dsn, $arr ) ) - { - $parsed[ "username" ] = $arr[ 1 ]; - $parsed[ "password" ] = $arr[ 2 ]; - $dsn = $arr[ 3 ]; + if (!$parsed["database"]) { + $dsn = preg_replace("|/+$|", "", $dsn); } - elseif( preg_match( "|^([^:]+)@(.*)$|", $dsn, $arr ) ) - { - $parsed[ "username" ] = $arr[ 1 ]; - $dsn = $arr[ 2 ]; - } + $parsed["hostspec"] = urldecode($dsn); - if( preg_match( "|^([^\+]+)\+(.*)$|", $dsn, $arr ) ) - { - $parsed[ "protocol" ] = $arr[ 1 ]; - $dsn = $arr[ 2 ]; + if(!$parsed["dbsyntax"]) { + $parsed["dbsyntax"] = $parsed["phptype"]; } - if( !$parsed[ "database" ] ) - { - $dsn = preg_replace( "|/+$|", "", $dsn ); - } + return $parsed; + } +} - $parsed[ "hostspec" ] = urldecode( $dsn ); +/** + * DB_Error implements a class for reporting portable database error + * messages. + * + * @author Stig Bakken <ssb@fast.no> + */ +class DB_Error extends PEAR_Error +{ + /** + * DB_Error constructor. + * + * @param $code mixed DB error code, or string with error message. + * @param $mode int what "error mode" to operate in + * @param $level what error level to use for $mode & PEAR_ERROR_TRIGGER + * @param $debuginfo additional debug info, such as the last query + * + * @access public + * + * @see PEAR_Error + */ - if( !$parsed[ "dbsyntax"] ) - { - $parsed[ "dbsyntax" ] = $parsed[ "phptype" ]; - } + 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", 0, $mode, $level, $debuginfo); + } + } +} - return $parsed; +/** + * DB_Warning implements a class for reporting portable database + * warning messages. + * + * @author Stig Bakken <ssb@fast.no> + */ +class DB_Warning extends PEAR_Error +{ + /** + * DB_Warning constructor. + * + * @param $code mixed DB error code, or string with error message. + * @param $mode int what "error mode" to operate in + * @param $level what error level to use for $mode == PEAR_ERROR_TRIGGER + * @param $debuginfo additional debug info, such as the last query + * + * @access public + * + * @see PEAR_Error + */ + + function DB_Warning($code = DB_WARNING, $mode = PEAR_ERROR_RETURN, + $level = E_USER_NOTICE, $debuginfo = null) { + if (is_int($code)) { + $this->PEAR_Error("DB Warning: " . DB::errorMessage( $code ), $code, $mode, $level, $debuginfo); + } else { + $this->PEAR_Error("DB Warning: $code", 0, $mode, $level, $debuginfo); } } +} + +/** + * This class implements a wrapper for a DB result set. + * A new instance of this class will be returned by the DB implementation + * after processing a query that returns data. + * + * @author Stig Bakken <ssb@fast.no> + */ + +class DB_result +{ + var $dbh; + var $result; + + /** + * DB_result constructor. + * @param $dbh DB object reference + * @param $result result resource id + */ + + function DB_result(&$dbh, $result) { + $this->dbh = &$dbh; + $this->result = $result; + } + + /** + * Fetch and return a row of data. + * @return array a row of data, or false on error + */ + function fetchRow($fetchmode = DB_FETCHMODE_DEFAULT) { + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->dbh->fetchmode; + } + return $this->dbh->fetchRow($this->result, $fetchmode); + } + + /** + * Fetch a row of data into an existing array. + * + * @param $arr reference to data array + * @return int error code + */ + function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT) { + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->dbh->fetchmode; + } + return $this->dbh->fetchInto($this->result, $arr, $fetchmode); + } + + /** + * Get the the number of columns in a result set. + * + * @return int the number of columns, or a DB error code + */ + function numCols() { + return $this->dbh->numCols($this->result); + } + + /** + * Frees the resources allocated for this result set. + * @return int error code + */ + function free() { + $err = $this->dbh->freeResult($this->result); + if( DB::isError($err)) { + return $err; + } + $this->result = false; + return true; + } +} + ?>
Index: php4/pear/DB/mysql.php diff -u php4/pear/DB/mysql.php:1.30 php4/pear/DB/mysql.php:1.31 --- php4/pear/DB/mysql.php:1.30 Mon Dec 4 09:13:30 2000 +++ php4/pear/DB/mysql.php Mon Dec 4 23:00:57 2000 @@ -28,594 +28,551 @@ // be registered here. // - require_once "DB/common.php"; +require_once "DB/common.php"; - class DB_mysql extends DB_common - { +class DB_mysql extends DB_common +{ // {{{ properties - var $connection; - var $phptype, $dbsyntax; - var $prepare_tokens = array(); - var $prepare_types = array(); + var $connection; + var $phptype, $dbsyntax; + var $prepare_tokens = array(); + var $prepare_types = array(); // }}} // {{{ constructor - /** - * DB_mysql constructor. - * - * @access public - */ - - function DB_mysql() - { - $this->phptype = "mysql"; - $this->dbsyntax = "mysql"; - $this->features = array( - "prepare" => false, - "pconnect" => true, - "transactions" => false - ); - - $this->errorcode_map = array( - 1004 => DB_ERROR_CANNOT_CREATE, - 1005 => DB_ERROR_CANNOT_CREATE, - 1006 => DB_ERROR_CANNOT_CREATE, - 1007 => DB_ERROR_ALREADY_EXISTS, - 1008 => DB_ERROR_CANNOT_DROP, - 1046 => DB_ERROR_NODBSELECTED, - 1146 => DB_ERROR_NOSUCHTABLE, - 1064 => DB_ERROR_SYNTAX, - 1054 => DB_ERROR_NOSUCHFIELD, - 1062 => DB_ERROR_ALREADY_EXISTS, - 1051 => DB_ERROR_NOSUCHTABLE, - 1100 => DB_ERROR_NOT_LOCKED, - 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, - ); - } + /** + * DB_mysql constructor. + * + * @access public + */ + + function DB_mysql() { + $this->phptype = "mysql"; + $this->dbsyntax = "mysql"; + $this->features = array( + "prepare" => false, + "pconnect" => true, + "transactions" => false + ); + + $this->errorcode_map = array( + 1004 => DB_ERROR_CANNOT_CREATE, + 1005 => DB_ERROR_CANNOT_CREATE, + 1006 => DB_ERROR_CANNOT_CREATE, + 1007 => DB_ERROR_ALREADY_EXISTS, + 1008 => DB_ERROR_CANNOT_DROP, + 1046 => DB_ERROR_NODBSELECTED, + 1146 => DB_ERROR_NOSUCHTABLE, + 1064 => DB_ERROR_SYNTAX, + 1054 => DB_ERROR_NOSUCHFIELD, + 1062 => DB_ERROR_ALREADY_EXISTS, + 1051 => DB_ERROR_NOSUCHTABLE, + 1100 => DB_ERROR_NOT_LOCKED, + 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, + ); + } // }}} // {{{ connect() + + /** + * Connect to a database and log in as the specified user. + * + * @param $dsn the data source name (see DB::parseDSN for syntax) + * @param $persistent (optional) whether the connection should + * be persistent + * @access public + * @return int DB_OK on success, a DB error on failure + */ + + function connect(&$dsn, $persistent = false) { + if (is_array($dsn)) { + $dsninfo = &$dsn; + } else { + $dsninfo = DB::parseDSN($dsn); + } + + if (!$dsninfo || !$dsninfo["phptype"]) { + return $this->raiseError(); // XXX ERRORMSG + } + + $dbhost = $dsninfo["hostspec"] ? $dsninfo["hostspec"] : "localhost"; + $user = $dsninfo["username"]; + $pw = $dsninfo["password"]; + + $connect_function = $persistent ? "mysql_pconnect" : "mysql_connect"; + + if ($dbhost && $user && $pw) { + $conn = $connect_function($dbhost, $user, $pw); + } elseif ($dbhost && $user) { + $conn = $connect_function($dbhost, $user); + } elseif ($dbhost) { + $conn = $connect_function($dbhost); + } else { + $conn = false; + } + + if ($conn == false) { + return $this->raiseError(); // XXX ERRORMSG + } + + if ($dsninfo["database"]) { + if (!mysql_select_db($dsninfo["database"], $conn)) { + return $this->raiseError(); // XXX ERRORMSG + } + } - /** - * Connect to a database and log in as the specified user. - * - * @param $dsn the data source name (see DB::parseDSN for syntax) - * @param $persistent (optional) whether the connection should - * be persistent - * @access public - * @return int DB_OK on success, a DB error on failure - */ - - function connect( &$dsn, $persistent = false ) - { - if( is_array( $dsn ) ) - { - $dsninfo = &$dsn; - } - - else - { - $dsninfo = DB::parseDSN( $dsn ); - } - - if( !$dsninfo || !$dsninfo[ "phptype" ] ) - { - return $this->raiseError(); // XXX ERRORMSG - } - - $dbhost = $dsninfo[ "hostspec" ] ? $dsninfo[ "hostspec" ] : "localhost"; - $user = $dsninfo[ "username" ]; - $pw = $dsninfo[ "password" ]; - - $connect_function = $persistent ? "mysql_pconnect" : "mysql_connect"; - - if( $dbhost && $user && $pw ) - { - $conn = $connect_function( $dbhost, $user, $pw ); - } - - elseif( $dbhost && $user ) - { - $conn = $connect_function( $dbhost, $user ); - } - - elseif( $dbhost ) - { - $conn = $connect_function( $dbhost ); - } - - else - { - $conn = false; - } - - if( $conn == false ) - { - return $this->raiseError(); // XXX ERRORMSG - } - - if( $dsninfo[ "database" ] ) - { - if( !mysql_select_db( $dsninfo[ "database" ], $conn ) ) - { - return $this->raiseError(); // XXX ERRORMSG - } - } - - $this->connection = $conn; - return DB_OK; - } + $this->connection = $conn; + return DB_OK; + } // }}} // {{{ disconnect() - /** - * Log out and disconnect from the database. - * - * @access public - * - * @return bool TRUE on success, FALSE if not connected. - */ - - function disconnect() - { - return mysql_close( $this->connection ); // XXX ERRORMSG - } + /** + * Log out and disconnect from the database. + * + * @access public + * + * @return bool TRUE on success, FALSE if not connected. + */ + function disconnect() { + return mysql_close($this->connection); + } // }}} // {{{ query() - - /** - * Send a query to MySQL and return the results as a DB_result object. - * - * @param $query the SQL query - * - * @access public - * - * @return object a DB_result object on success, a DB error - * on failure - */ - - function &query( $query ) - { - $this->last_query = $query; - - $result = @mysql_query( $query, $this->connection ); - - if( !$result ) - { - return $this->raiseError( $this->errorCode( mysql_errno( $this->connection ) ) ); - } - - // Determine which queries that should return data, and which - // should return an error code only. - if( preg_match( "/(SELECT|SHOW)/i", $query ) ) - { - return new DB_result( $this, $result ); - } - - else - { - return DB_OK; - } - } + /** + * Send a query to MySQL and return the results as a DB_result object. + * + * @param $query the SQL query + * + * @access public + * + * @return object a DB_result object on success, a DB error + * on failure + */ + function &query($query) { + $this->last_query = $query; + + $result = @mysql_query( $query, $this->connection ); + + if (!$result) { + return $this->raiseError($this->errorCode(mysql_errno($this->connection))); + } + + // Determine which queries that should return data, and which + // should return an error code only. + if (preg_match("/(SELECT|SHOW)/i", $query)) { + return new DB_result($this, $result); + } else { + return DB_OK; + } + } - // }}} + // }}} // {{{ simpleQuery() - /** - * Send a query to MySQL and return the results as a MySQL resource - * identifier. - * - * @param $query the SQL query - * - * @access public - * - * @return int returns a valid MySQL result for successful SELECT - * queries, DB_OK for other successful queries. A DB error is - * returned on failure. - */ - - function simpleQuery( $query ) - { - $this->last_query = $query; - $result = mysql_query( $query, $this->connection ); - - if( !$result ) - { - return $this->raiseError( $this->errorCode( mysql_errno( $this->connection ) ) ); - } - - // Determine which queries that should return data, and which - // should return an error code only. - if( preg_match( "/(SELECT|SHOW)/i", $query ) ) - { - return $result; - } - - else - { - return DB_OK; - } - } + /** + * Send a query to MySQL and return the results as a MySQL resource + * identifier. + * + * @param $query the SQL query + * + * @access public + * + * @return int returns a valid MySQL result for successful SELECT + * queries, DB_OK for other successful queries. A DB error is + * returned on failure. + */ + function simpleQuery($query) { + $this->last_query = $query; + $result = mysql_query( $query, $this->connection ); + + if (!$result) { + return $this->raiseError($this->errorCode(mysql_errno($this->connection))); + } + + // Determine which queries that should return data, and which + // should return an error code only. + if (preg_match("/(SELECT|SHOW)/i", $query)) { + return $result; + } else { + return DB_OK; + } + } - // }}} + // }}} // {{{ fetchRow() - /** - * Fetch a row and return as array. - * - * @param $result MySQL result identifier - * @param $fetchmode how the resulting array should be indexed - * - * @access public - * - * @return mixed an array on success, a DB error on failure, NULL - * if there is no more data - */ - - function &fetchRow( $result, $fetchmode = DB_FETCHMODE_DEFAULT ) - { - if( $fetchmode == DB_FETCHMODE_DEFAULT ) - { - $fetchmode = $this->fetchmode; - } - - if( $fetchmode & DB_FETCHMODE_ASSOC ) - { - $row = mysql_fetch_array( $result, MYSQL_ASSOC ); - } - - else - { - $row = mysql_fetch_row( $result ); - } - - if( !$row ) - { - $errno = mysql_errno( $this->connection ); - - if( !$errno ) - { - return NULL; - } - - return $this->raiseError( $this->errorCode( $errno ) ); - } - - return $row; - } + /** + * Fetch a row and return as array. + * + * @param $result MySQL result identifier + * @param $fetchmode how the resulting array should be indexed + * + * @access public + * + * @return mixed an array on success, a DB error on failure, NULL + * if there is no more data + */ + function &fetchRow($result, $fetchmode = DB_FETCHMODE_DEFAULT) { + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $row = mysql_fetch_array($result, MYSQL_ASSOC); + } else { + $row = mysql_fetch_row($result); + } + + if (!$row) { + $errno = mysql_errno($this->connection); + + if (!$errno) { + return NULL; + } + + return $this->raiseError($this->errorCode($errno)); + } + + return $row; + } - // }}} + // }}} // {{{ fetchInto() - /** - * Fetch a row and insert the data into an existing array. - * - * @param $result MySQL result identifier - * @param $arr (reference) array where data from the row is stored - * @param $fetchmode how the array data should be indexed - * - * @access public - * - * @return int DB_OK on success, a DB error on failure - */ - - function fetchInto($result, &$arr, $fetchmode = DB_FETCHMODE_DEFAULT ) - { - if( $fetchmode == DB_FETCHMODE_DEFAULT ) - { - $fetchmode = $this->fetchmode; - } - - if( $fetchmode & DB_FETCHMODE_ASSOC ) - { - $arr = mysql_fetch_array( $result, MYSQL_ASSOC ); - } - - else - { - $arr = mysql_fetch_row( $result ); - } - - if( !$arr ) - { - $errno = mysql_errno( $this->connection ); - - if( !$errno ) - { - return NULL; - } + /** + * Fetch a row and insert the data into an existing array. + * + * @param $result MySQL result identifier + * @param $arr (reference) array where data from the row is stored + * @param $fetchmode how the array data should be indexed + * + * @access public + * + * @return int DB_OK on success, a DB error on failure + */ + function fetchInto($result, &$arr, $fetchmode = DB_FETCHMODE_DEFAULT) { + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->fetchmode; + } + + if ($fetchmode & DB_FETCHMODE_ASSOC) { + $arr = mysql_fetch_array($result, MYSQL_ASSOC); + } else { + $arr = mysql_fetch_row($result); + } + + if (!$arr) { + $errno = mysql_errno($this->connection); + + if (!$errno) { + return NULL; + } - return $this->raiseError( $this->errorCode( $errno ) ); - } + return $this->raiseError($this->errorCode($errno)); + } - return DB_OK; - } + return DB_OK; + } - // }}} + // }}} // {{{ freeResult() - /** - * Free the internal resources associated with $result. - * - * @param $result MySQL result identifier or DB statement identifier - * - * @access public - * - * @return bool TRUE on success, FALSE if $result is invalid - */ - - function freeResult( $result ) - { - if( is_resource( $result ) ) - { - return mysql_free_result( $result ); - } - - if( !isset( $this->prepare_tokens[ $result ] ) ) - { - return false; - } + /** + * Free the internal resources associated with $result. + * + * @param $result MySQL result identifier or DB statement identifier + * + * @access public + * + * @return bool TRUE on success, FALSE if $result is invalid + */ + function freeResult($result) { + if (is_resource($result)) { + return mysql_free_result($result); + } + + if (!isset($this->prepare_tokens[$result])) { + return false; + } - unset( $this->prepare_tokens[ $result ] ); - unset( $this->prepare_types[ $result ] ); + unset($this->prepare_tokens[$result]); + unset($this->prepare_types[$result]); - return true; - } + return true; + } - // }}} + // }}} // {{{ numCols() - /** - * Get the number of columns in a result set. - * - * @param $result MySQL result identifier - * - * @access public - * - * @return int the number of columns per row in $result - */ - - function numCols( $result ) - { - $cols = mysql_num_fields( $result ); - - if( !$cols ) - { - return $this->raiseError( $this->errorCode( mysql_errno( $this->connection ) ) ); - } + /** + * Get the number of columns in a result set. + * + * @param $result MySQL result identifier + * + * @access public + * + * @return int the number of columns per row in $result + */ + function numCols($result) { + $cols = mysql_num_fields($result); + + if (!$cols) { + return $this->raiseError($this->errorCode(mysql_errno($this->connection))); + } - return $cols; - } + return $cols; + } // }}} // {{{ errorNative() + + /** + * Get the native error code of the last error (if any) that + * occured on the current connection. + * + * @access public + * + * @return int native MySQL error code + */ - /** - * Get the native error code of the last error (if any) that - * occured on the current connection. - * - * @access public - * - * @return int native MySQL error code - */ - - function errorNative() - { - return mysql_errno( $this->connection ); - } + function errorNative() { + return mysql_errno($this->connection); + } // }}} // {{{ prepare() - /** - * Prepares a query for multiple execution with execute(). With - * MySQL, this is emulated. - * - * @param $query the SQL query to prepare for execution. The - * characters "?" and "&" have special meanings. "?" is a scalar - * placeholder, the value provided for it when execute()ing is - * inserted as-is. "&" is an opaque placeholder, the value - * provided on execute() is a file name, and the contents of that - * file will be used in the query. - * - * @access public - * - * @return int identifier for this prepared query - */ - - function prepare( $query ) - { - $tokens = split( "[\&\?]", $query ); - $token = 0; - $types = array(); - - for( $i = 0; $i < strlen( $query ); $i++ ) - { - switch( $query[ $i ] ) - { - case "?": $types[ $token++ ] = DB_PARAM_SCALAR; - - break; - case '&': $types[ $token++ ] = DB_PARAM_OPAQUE; - - break; - } - } + /** + * Prepares a query for multiple execution with execute(). With + * MySQL, this is emulated. + * + * @param $query the SQL query to prepare for execution. The + * characters "?" and "&" have special meanings. "?" is a scalar + * placeholder, the value provided for it when execute()ing is + * inserted as-is. "&" is an opaque placeholder, the value + * provided on execute() is a file name, and the contents of that + * file will be used in the query. + * + * @access public + * + * @return int identifier for this prepared query + */ + + function prepare($query) { + $tokens = split("[\&\?]", $query); + $token = 0; + $types = array(); + + for ($i = 0; $i < strlen($query); $i++) { + switch ($query[$i]) { + case "?": + $types[$token++] = DB_PARAM_SCALAR; + break; + case '&': + $types[$token++] = DB_PARAM_OPAQUE; + break; + } + } - $this->prepare_tokens[] = &$tokens; - end( $this->prepare_tokens ); + $this->prepare_tokens[] = &$tokens; + end($this->prepare_tokens); - $k = key( $this->prepare_tokens ); - $this->prepare_types[ $k ] = $types; + $k = key($this->prepare_tokens); + $this->prepare_types[$k] = $types; - return $k; - } + return $k; + } // }}} // {{{ execute() - /** - * Executes a prepared query and substitutes placeholders with - * provided values. - * - * @return int returns a MySQL result resource for successful - * SELECT queries, DB_OK for other successful queries. A DB error - * is returned on failure. - * - * @see DB_mysql::prepare - */ - - function execute( $stmt, $data = false ) - { - $realquery = $this->execute_emulate_query( $stmt, $data ); - $this->last_query = $realquery; - - $result = mysql_query( $realquery, $this->connection ); - - if( !$result ) - { - return $this->raiseError( $this->errorCode( mysql_errno( $this->connection ) ) ); - } - - if( preg_match( "/(SELECT|SHOW)/i", $realquery ) ) - { - return $result; - } - - else - { - return DB_OK; - } - } + /** + * Executes a prepared query and substitutes placeholders with + * provided values. + * + * @return int returns a MySQL result resource for successful + * SELECT queries, DB_OK for other successful queries. A DB error + * is returned on failure. + * + * @see DB_mysql::prepare + */ + function execute($stmt, $data = false) { + $realquery = $this->execute_emulate_query($stmt, $data); + $this->last_query = $realquery; + + $result = mysql_query($realquery, $this->connection); + + if (!$result) { + return $this->raiseError($this->errorCode(mysql_errno($this->connection))); + } + + if (preg_match("/(SELECT|SHOW)/i", $realquery)) { + return $result; + } else { + return DB_OK; + } + } // }}} // {{{ autoCommit() - /** - * Enable/disable automatic commits [not supported by MySQL] - * - * @access public - * - * @return object DB error code (not capable) - */ - - function autoCommit( $onoff = false ) - { - return $this->raiseError( DB_ERROR_NOT_CAPABLE ); - } + /** + * Enable/disable automatic commits [not supported by MySQL] + * + * @access public + * + * @return object DB error code (not capable) + */ + function autoCommit($onoff = false) { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } // }}} // {{{ commit() - /** - * Commit transactions on the current connection [not supported by MySQL] - * - * @access public - * - * @return object DB error code (not capable) - */ - - function commit() - { - return $this->raiseError( DB_ERROR_NOT_CAPABLE ); - } + /** + * Commit transactions on the current connection [not supported by MySQL] + * + * @access public + * + * @return object DB error code (not capable) + */ + function commit() { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } // }}} // {{{ rollback() - /** - * Roll back all uncommitted transactions on the current connection. - * [not supported by MySQL] - * - * @access public - * - * @return object DB error code (not capable) - */ - - function rollback() - { - return $this->raiseError( DB_ERROR_NOT_CAPABLE ); - } + /** + * Roll back all uncommitted transactions on the current connection. + * [not supported by MySQL] + * + * @access public + * + * @return object DB error code (not capable) + */ + function rollback() { + return $this->raiseError(DB_ERROR_NOT_CAPABLE); + } // }}} // {{{ lock() - function lock( $table, $mode = "write" ) - { - // build query string to lock tables - $query = "lock tables "; - - // parameter $table is an array - parse it - if( is_array( $table ) ) - { - // walk through array - while( list( $key, $value ) = each( $table ) ) - { - if( $key == "read" && $key != 0) - { - $query .= "$value read, "; - } - - else - { - $query .= "$value $mode, "; - } - } - - $query = substr( $query, 0, -2 ); - } - - // parameter $table is just one table name - else - { - $query .= "$table $mode"; - } - - // perform query to lock table(s) - $result = mysql_query( $query, $this->connection ); - - // check, if query succeeded - if( !$result ) - { - // query failed - return false; - } + function lock($table, $mode = "write") { + // build query string to lock tables + $query = "lock tables "; + + // parameter $table is an array - parse it + if (is_array($table)) { + // walk through array + while (list($key, $value) = each($table)) { + if ($key == "read" && $key != 0) { + $query .= "$value read, "; + } else { + $query .= "$value $mode, "; + } + } + + $query = substr($query, 0, -2); + } + // parameter $table is just one table name + else { + $query .= "$table $mode"; + } + + // perform query to lock table(s) + $result = mysql_query($query, $this->connection); + + // check, if query succeeded + if (!$result) { + // query failed + return false; + } - // query succeeded - return true; + // query succeeded + return true; } // }}} - // {{{ lock() + // {{{ unlock() + + function unlock() { + // perform query to unlock tables + $result = mysql_query("unlock tables", $this->connection); + + // check, if query succeeded + if (!$result) { + // query failed + return false; + } + + // query succeeded + return true; + } - function unlock() - { - // perform query to unlock tables - $result = mysql_query( "unlock tables", $this->connection ); + // }}} + // {{{ nextId() - // check, if query succeeded - if( !$result ) - { - // query failed - return false; - } + /** + * Get the next value in a sequence. We emulate sequences + * for MySQL. Will create the sequence if it does not exist. + * + * @access public + * + * @param $seq_name the name of the sequence + * + * @param $ondemand whether to create the sequence table on demand + * (default is true) + * + * @return a sequence integer, or a DB error + */ + function nextId($seq_name, $ondemand = true) { + $sqn = preg_replace('/[^a-z0-9_]/i', '_', $seq_name); + $repeat = 0; + do { + $result = $this->query("INSERT INTO db_seq_${sqn} VALUES(NULL)"); + if ($ondemand && DB::isError($result) && + $result->getCode() == DB_ERROR_NOSUCHTABLE) { + $repeat = 1; + $result = $this->createSequence($seq_name); + if (DB::isError($result)) { + return $result; + } + } else { + $repeat = 0; + } + } while ($repeat); + if (DB::isError($result)) { + return $result; + } + return mysql_insert_id($this->connection); + } + + // }}} + // {{{ createSequence() - // query succeeded - return true; + function createSequence($seq_name) { + $sqn = preg_replace('/[^a-z0-9_]/i', '_', $seq_name); + return $this->query("CREATE TABLE db_seq_${sqn} ". + "(id INTEGER AUTO_INCREMENT, PRIMARY KEY(id))"); } // }}} - // TODO/wishlist: - // simpleFetch - // simpleGet - // affectedRows + // TODO/wishlist: + // simpleFetch + // simpleGet + // affectedRows // longReadlen // binmode - } +} + ?> Index: php4/pear/DB.php diff -u php4/pear/DB.php:1.37 php4/pear/DB.php:1.38 --- php4/pear/DB.php:1.37 Mon Dec 4 09:13:29 2000 +++ php4/pear/DB.php Mon Dec 4 23:00:57 2000 @@ -17,150 +17,147 @@ // | | // +----------------------------------------------------------------------+ // -// $Id: DB.php,v 1.37 2000/12/04 17:13:29 sbergmann Exp $ +// $Id: DB.php,v 1.38 2000/12/05 07:00:57 ssb Exp $ // // Database independent query interface. // - require_once "PEAR.php"; - require_once "DB/ERROR.php"; - require_once "DB/RESULT.php"; - require_once "DB/WARNING.php"; - - /* - * The method mapErrorCode in each DB_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", 0 ); - define( "DB_ERROR", -1 ); - define( "DB_ERROR_SYNTAX", -2 ); - define( "DB_ERROR_CONSTRAINT", -3 ); - define( "DB_ERROR_NOT_FOUND", -4 ); - define( "DB_ERROR_ALREADY_EXISTS", -5 ); - define( "DB_ERROR_UNSUPPORTED", -6 ); - define( "DB_ERROR_MISMATCH", -7 ); - define( "DB_ERROR_INVALID", -8 ); - define( "DB_ERROR_NOT_CAPABLE", -9 ); - define( "DB_ERROR_TRUNCATED", -10 ); - define( "DB_ERROR_INVALID_NUMBER", -11 ); - define( "DB_ERROR_INVALID_DATE", -12 ); - define( "DB_ERROR_DIVZERO", -13 ); - define( "DB_ERROR_NODBSELECTED", -14 ); - define( "DB_ERROR_CANNOT_CREATE", -15 ); - define( "DB_ERROR_CANNOT_DELETE", -16 ); - define( "DB_ERROR_CANNOT_DROP", -17 ); - define( "DB_ERROR_NOSUCHTABLE", -18 ); - define( "DB_ERROR_NOSUCHFIELD", -19 ); - define( "DB_ERROR_NEED_MORE_DATA", -20 ); - define( "DB_ERROR_NOT_LOCKED", -21 ); - define( "DB_ERROR_VALUE_COUNT_ON_ROW", -22 ); - - /* - * Warnings are not detected as errors by DB::isError(), and are not - * fatal. You can detect whether an error is in fact a warning with - * DB::isWarning(). - */ - - define( "DB_WARNING", -1000 ); - define( "DB_WARNING_READ_ONLY", -1001 ); - - /* - * These constants are used when storing information about prepared - * statements (using the "prepare" method in DB_dbtype). - * - * The prepare/execute model in DB is mostly borrowed from the ODBC - * extension, in a query the "?" character means a scalar parameter. - * There is one extension though, a "*" character means an opaque - * parameter. An opaque parameter is simply a file name, the real - * data are in that file (useful for stuff like putting uploaded files - * into your database). - */ - - define( "DB_PARAM_SCALAR", 1 ); - define( "DB_PARAM_OPAQUE", 2 ); - - /* - * These constants define different ways of returning binary data - * from queries. Again, this model has been borrowed from the ODBC - * extension. - * - * DB_BINMODE_PASSTHRU sends the data directly through to the browser - * when data is fetched from the database. - * DB_BINMODE_RETURN lets you return data as usual. - * DB_BINMODE_CONVERT returns data as well, only it is converted to - * hex format, for example the string "123" would become "313233". - */ - - define( "DB_BINMODE_PASSTHRU", 1 ); - define( "DB_BINMODE_RETURN", 2 ); - define( "DB_BINMODE_CONVERT", 3 ); - - /** - * This is a special constant that tells DB the user hasn't specified - * any particular get mode, so the default should be used. - */ - - define( "DB_FETCHMODE_DEFAULT", 0 ); - - /** - * Column data indexed by numbers, ordered from 0 and up - */ - - define( "DB_FETCHMODE_ORDERED", 1 ); - - /** - * Column data indexed by column names - */ - - define( "DB_FETCHMODE_ASSOC", 2 ); - - /** - * For multi-dimensional results: normally the first level of arrays - * is the row number, and the second level indexed by column number or name. - * DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays - * is the column name, and the second level the row number. - */ - - define( "DB_FETCHMODE_FLIPPED", 4 ); - - /* for compatibility */ - - define( "DB_GETMODE_ORDERED", DB_FETCHMODE_ORDERED ); - define( "DB_GETMODE_ASSOC", DB_FETCHMODE_ASSOC ); - define( "DB_GETMODE_FLIPPED", DB_FETCHMODE_FLIPPED ); - - /** - * The main "DB" class is simply a container class with some static - * methods for creating DB objects as well as some utility functions - * common to all parts of DB. - * - * The object model of DB is as follows (indentation means inheritance): - * - * DB The main DB class. This is simply a utility class - * with some "static" methods for creating DB objects as - * well as common utility functions for other DB classes. - * - * DB_common The base for each DB implementation. Provides default - * | implementations (in OO lingo virtual methods) for - * | the actual DB implementations as well as a bunch of - * | query utility functions. - * | - * +-DB_mysql The DB implementation for MySQL. Inherits DB_common. - * When calling DB::factory or DB::connect for MySQL - * connections, the object returned is an instance of this - * class. - * - * @version 2 - * @author Stig Bakken <ssb@fast.no> - * @since PHP 4.0 - */ +require_once "PEAR.php"; - class DB - { +/* + * The method mapErrorCode in each DB_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", 0); +define("DB_ERROR", -1); +define("DB_ERROR_SYNTAX", -2); +define("DB_ERROR_CONSTRAINT", -3); +define("DB_ERROR_NOT_FOUND", -4); +define("DB_ERROR_ALREADY_EXISTS", -5); +define("DB_ERROR_UNSUPPORTED", -6); +define("DB_ERROR_MISMATCH", -7); +define("DB_ERROR_INVALID", -8); +define("DB_ERROR_NOT_CAPABLE", -9); +define("DB_ERROR_TRUNCATED", -10); +define("DB_ERROR_INVALID_NUMBER", -11); +define("DB_ERROR_INVALID_DATE", -12); +define("DB_ERROR_DIVZERO", -13); +define("DB_ERROR_NODBSELECTED", -14); +define("DB_ERROR_CANNOT_CREATE", -15); +define("DB_ERROR_CANNOT_DELETE", -16); +define("DB_ERROR_CANNOT_DROP", -17); +define("DB_ERROR_NOSUCHTABLE", -18); +define("DB_ERROR_NOSUCHFIELD", -19); +define("DB_ERROR_NEED_MORE_DATA", -20); +define("DB_ERROR_NOT_LOCKED", -21); +define("DB_ERROR_VALUE_COUNT_ON_ROW", -22); + +/* + * Warnings are not detected as errors by DB::isError(), and are not + * fatal. You can detect whether an error is in fact a warning with + * DB::isWarning(). + */ + +define("DB_WARNING", -1000); +define("DB_WARNING_READ_ONLY", -1001); + +/* + * These constants are used when storing information about prepared + * statements (using the "prepare" method in DB_dbtype). + * + * The prepare/execute model in DB is mostly borrowed from the ODBC + * extension, in a query the "?" character means a scalar parameter. + * There is one extension though, a "*" character means an opaque + * parameter. An opaque parameter is simply a file name, the real + * data are in that file (useful for stuff like putting uploaded files + * into your database). + */ + +define("DB_PARAM_SCALAR", 1); +define("DB_PARAM_OPAQUE", 2); + +/* + * These constants define different ways of returning binary data + * from queries. Again, this model has been borrowed from the ODBC + * extension. + * + * DB_BINMODE_PASSTHRU sends the data directly through to the browser + * when data is fetched from the database. + * DB_BINMODE_RETURN lets you return data as usual. + * DB_BINMODE_CONVERT returns data as well, only it is converted to + * hex format, for example the string "123" would become "313233". + */ + +define("DB_BINMODE_PASSTHRU", 1); +define("DB_BINMODE_RETURN", 2); +define("DB_BINMODE_CONVERT", 3); + +/** + * This is a special constant that tells DB the user hasn't specified + * any particular get mode, so the default should be used. + */ + +define("DB_FETCHMODE_DEFAULT", 0); + +/** + * Column data indexed by numbers, ordered from 0 and up + */ + +define("DB_FETCHMODE_ORDERED", 1); + +/** + * Column data indexed by column names + */ + +define("DB_FETCHMODE_ASSOC", 2); + +/** + * For multi-dimensional results: normally the first level of arrays + * is the row number, and the second level indexed by column number or name. + * DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays + * is the column name, and the second level the row number. + */ + +define("DB_FETCHMODE_FLIPPED", 4); + +/* for compatibility */ + +define("DB_GETMODE_ORDERED", DB_FETCHMODE_ORDERED); +define("DB_GETMODE_ASSOC", DB_FETCHMODE_ASSOC); +define("DB_GETMODE_FLIPPED", DB_FETCHMODE_FLIPPED); + +/** + * The main "DB" class is simply a container class with some static + * methods for creating DB objects as well as some utility functions + * common to all parts of DB. + * + * The object model of DB is as follows (indentation means inheritance): + * + * DB The main DB class. This is simply a utility class + * with some "static" methods for creating DB objects as + * well as common utility functions for other DB classes. + * + * DB_common The base for each DB implementation. Provides default + * | implementations (in OO lingo virtual methods) for + * | the actual DB implementations as well as a bunch of + * | query utility functions. + * | + * +-DB_mysql The DB implementation for MySQL. Inherits DB_common. + * When calling DB::factory or DB::connect for MySQL + * connections, the object returned is an instance of this + * class. + * + * @version 2 + * @author Stig Bakken <ssb@fast.no> + * @since PHP 4.0 + */ + +class DB +{ /** * Create a new DB object for the specified database type * @@ -170,26 +167,26 @@ * error */ - function &factory( $type ) + function &factory($type) { - @include_once( "DB/${type}.php" ); + @include_once("DB/${type}.php"); - $classname = "DB_" . $type; - $obj = @new $classname; + $classname = "DB_" . $type; + $obj = @new $classname; - if( !$obj ) - { - return new DB_Error( DB_ERROR_NOT_FOUND ); - } + if (!$obj) { + return new DB_Error(DB_ERROR_NOT_FOUND); + } - return $obj; + return $obj; } /** * Create a new DB object and connect to the specified database * - * @param $dsn string "data source name", see the DB::parseDSN - * method for a description of the dsn format. + * @param $dsn mixed "data source name", see the DB::parseDSN + * method for a description of the dsn format. Can also be + * specified as an array of the format returned by DB::parseDSN. * * @param $persistent bool whether this connection should be * persistent. Ignored if the backend extension does not support @@ -197,30 +194,33 @@ * * @return object a newly created DB object, or a DB error code on * error + * + * @see DB::parseDSN */ - - function &connect( $dsn, $persistent = false ) + function &connect($dsn, $persistent = false) { - $dsninfo = DB::parseDSN( $dsn ); - $type = $dsninfo[ "phptype" ]; - $classname = "DB_" . $type; - - @include_once( "DB/${type}.php" ); - $obj = @new $classname; - - if( !$obj ) - { - return new DB_Error( DB_ERROR_NOT_FOUND ); - } + if (is_array($dsn)) { + $dsninfo = $dsn; + } else { + $dsninfo = DB::parseDSN($dsn); + } + $type = $dsninfo["phptype"]; + $classname = "DB_" . $type; + + @include_once("DB/${type}.php"); + $obj = @new $classname; + + if (!$obj) { + return new DB_Error(DB_ERROR_NOT_FOUND); + } + + $err = $obj->connect($dsninfo, $persistent); + + if (DB::isError($err)) { + return $err; + } - $err = $obj->connect( $dsninfo, $persistent ); - - if( DB::isError( $err ) ) - { - return $err; - } - - return $obj; + return $obj; } /** @@ -228,10 +228,9 @@ * * @return int the DB API version number */ - function apiVersion() { - return 2; + return 2; } /** @@ -241,13 +240,11 @@ * * @return bool whether $value is an error */ - - function isError( $value ) + function isError($value) { - return is_object( $value ) && - ( get_class( $value ) == "db_error" || - is_subclass_of( $value, "db_error" ) - ); + return is_object($value) && + (get_class($value) == "db_error" || + is_subclass_of($value, "db_error")); } /** @@ -259,13 +256,11 @@ * * @return bool whether $value is a warning */ - - function isWarning( $value ) + function isWarning($value) { - return is_object( $value ) && - ( get_class( $value ) == "db_warning" || - is_subclass_of( $value, "db_warning" ) - ); + return is_object($value) && + (get_class( $value ) == "db_warning" || + is_subclass_of($value, "db_warning")); } /** @@ -276,44 +271,41 @@ * @return string error message, or false if the error code was * not recognized */ - - function errorMessage( $value ) + function errorMessage($value) { - if( !isset( $errorMessages ) ) - { - $errorMessages = array( - DB_ERROR => "unknown error", - DB_ERROR_ALREADY_EXISTS => "already exists", - DB_ERROR_CANNOT_CREATE => "can not create", - DB_ERROR_CANNOT_DELETE => "can not delete", - DB_ERROR_CANNOT_DROP => "can not drop", - DB_ERROR_CONSTRAINT => "constraint violation", - DB_ERROR_DIVZERO => "division by zero", - DB_ERROR_INVALID => "invalid", - DB_ERROR_INVALID_DATE => "invalid date or time", - DB_ERROR_INVALID_NUMBER => "invalid number", - DB_ERROR_MISMATCH => "mismatch", - DB_ERROR_NODBSELECTED => "no database selected", - DB_ERROR_NOSUCHFIELD => "no such field", - DB_ERROR_NOSUCHTABLE => "no such table", - DB_ERROR_NOT_CAPABLE => "DB backend not capable", - DB_ERROR_NOT_FOUND => "not found", - DB_ERROR_NOT_LOCKED => "not locked", - DB_ERROR_SYNTAX => "syntax error", - DB_ERROR_UNSUPPORTED => "not supported", - DB_ERROR_VALUE_COUNT_ON_ROW => "value count on row", - DB_OK => "no error", - DB_WARNING => "unknown warning", - DB_WARNING_READ_ONLY => "read only" - ); - } + if (!isset($errorMessages)) { + $errorMessages = array( + DB_ERROR => "unknown error", + DB_ERROR_ALREADY_EXISTS => "already exists", + DB_ERROR_CANNOT_CREATE => "can not create", + DB_ERROR_CANNOT_DELETE => "can not delete", + DB_ERROR_CANNOT_DROP => "can not drop", + DB_ERROR_CONSTRAINT => "constraint violation", + DB_ERROR_DIVZERO => "division by zero", + DB_ERROR_INVALID => "invalid", + DB_ERROR_INVALID_DATE => "invalid date or time", + DB_ERROR_INVALID_NUMBER => "invalid number", + DB_ERROR_MISMATCH => "mismatch", + DB_ERROR_NODBSELECTED => "no database selected", + DB_ERROR_NOSUCHFIELD => "no such field", + DB_ERROR_NOSUCHTABLE => "no such table", + DB_ERROR_NOT_CAPABLE => "DB backend not capable", + DB_ERROR_NOT_FOUND => "not found", + DB_ERROR_NOT_LOCKED => "not locked", + DB_ERROR_SYNTAX => "syntax error", + DB_ERROR_UNSUPPORTED => "not supported", + DB_ERROR_VALUE_COUNT_ON_ROW => "value count on row", + DB_OK => "no error", + DB_WARNING => "unknown warning", + DB_WARNING_READ_ONLY => "read only" + ); + } + + if (DB::isError($value)) { + $value = $value->code; + } - if( DB::isError( $value ) ) - { - $value = $value->code; - } - - return $errorMessages[ $value ]; + return $errorMessages[$value]; } /** @@ -322,135 +314,233 @@ * @param $dsn string Data Source Name to be parsed * * @return array an associative array with the following keys: - * <dl> - * <dt>phptype</dt> - * <dd>Database backend used in PHP (mysql, odbc etc.)</dd> - * <dt>dbsyntax</dt> - * <dd>Database used with regards to SQL syntax etc.</dd> - * <dt>protocol</dt> - * <dd>Communication protocol to use (tcp, unix etc.)</dd> - * <dt>hostspec</dt> - * <dd>Host specification (hostname[:port])</dd> - * <dt>database</dt> - * <dd>Database to use on the DBMS server</dd> - * <dt>username</dt> - * <dd>User name for login</dd> - * <dt>password</dt> - * <dd>Password for login</dd> - * </dl> - * </p> * - * <p> + * phptype: Database backend used in PHP (mysql, odbc etc.) + * dbsyntax: Database used with regards to SQL syntax etc. + * protocol: Communication protocol to use (tcp, unix etc.) + * hostspec: Host specification (hostname[:port]) + * database: Database to use on the DBMS server + * username: User name for login + * password: Password for login + * * The format of the supplied DSN is in its fullest form: - * <ul> - * <li>phptype(dbsyntax)://username:password@protocol+hostspec/database</li> - * </ul> + * + * phptype(dbsyntax)://username:password@protocol+hostspec/database + * * Most variations are allowed: - * <ul> - * <li>phptype://username:password@protocol+hostspec/database</li> - * <li>phptype://username:password@hostspec/database</li> - * <li>phptype://username:password@hostspec</li> - * <li>phptype://hostspec/database</li> - * <li>phptype://hostspec</li> - * <li>phptype(dbsyntax)</li> - * <li>phptype</li> - * </ul> - * </p> + * phptype://username:password@protocol+hostspec/database</li> + * phptype://username:password@hostspec/database</li> + * phptype://username:password@hostspec</li> + * phptype://hostspec/database</li> + * phptype://hostspec</li> + * phptype(dbsyntax)</li> + * phptype</li> * * @return bool FALSE is returned on error */ - - function parseDSN( $dsn ) + function parseDSN($dsn) { - if( is_array( $dsn ) ) - { - return $dsn; - } + if (is_array($dsn)) { + return $dsn; + } - $parsed = array( - "phptype" => false, - "dbsyntax" => false, - "protocol" => false, - "hostspec" => false, - "database" => false, - "username" => false, - "password" => false - ); - - if( preg_match( "|^([^:]+)://|", $dsn, $arr ) ) - { - $dbtype = $arr[ 1 ]; - $dsn = preg_replace( "|^[^:]+://|", '', $dsn ); - - // match "phptype(dbsyntax)" - if( preg_match( "|^([^\(]+)\((.+)\)$|", $dbtype, $arr ) ) - { - $parsed[ "phptype" ] = $arr[ 1 ]; - $parsed[ "dbsyntax" ] = $arr[ 2 ]; - } + $parsed = array( + "phptype" => false, + "dbsyntax" => false, + "protocol" => false, + "hostspec" => false, + "database" => false, + "username" => false, + "password" => false + ); + + if (preg_match("|^([^:]+)://|", $dsn, $arr)) { + $dbtype = $arr[ 1 ]; + $dsn = preg_replace( "|^[^:]+://|", '', $dsn); + + // match "phptype(dbsyntax)" + if (preg_match("|^([^\(]+)\((.+)\)$|", $dbtype, $arr)) { + $parsed["phptype"] = $arr[1]; + $parsed["dbsyntax"] = $arr[2]; + } else { + $parsed["phptype"] = $dbtype; + } + } else { + // match "phptype(dbsyntax)" + if (preg_match("|^([^\(]+)\((.+)\)$|", $dsn, $arr)) { + $parsed["phptype"] = $arr[1]; + $parsed["dbsyntax"] = $arr[2]; + } else { + $parsed["phptype"] = $dsn; + } - else - { - $parsed[ "phptype" ] = $dbtype; + return $parsed; } + if (preg_match("|^(.*)/([^/]+)/?$|", $dsn, $arr)) { + $parsed["database"] = $arr[2]; + $dsn = $arr[1]; } - else - { - // match "phptype(dbsyntax)" - if( preg_match( "|^([^\(]+)\((.+)\)$|", $dsn, $arr ) ) - { - $parsed[ "phptype" ] = $arr[ 1 ]; - $parsed[ "dbsyntax" ] = $arr[ 2 ]; - } - - else - { - $parsed[ "phptype" ] = $dsn; - } - - return $parsed; + if (preg_match("|^([^:]+):([^@]+)@?(.*)$|", $dsn, $arr)) { + $parsed["username"] = $arr[1]; + $parsed["password"] = $arr[2]; + $dsn = $arr[3]; + } elseif (preg_match("|^([^:]+)@(.*)$|", $dsn, $arr)) { + $parsed["username"] = $arr[1]; + $dsn = $arr[2]; } - if( preg_match( "|^(.*)/([^/]+)/?$|", $dsn, $arr ) ) - { - $parsed[ "database" ] = $arr[ 2 ]; - $dsn = $arr[ 1 ]; + if (preg_match("|^([^\+]+)\+(.*)$|", $dsn, $arr)) { + $parsed["protocol"] = $arr[1]; + $dsn = $arr[2]; } - if( preg_match( "|^([^:]+):([^@]+)@?(.*)$|", $dsn, $arr ) ) - { - $parsed[ "username" ] = $arr[ 1 ]; - $parsed[ "password" ] = $arr[ 2 ]; - $dsn = $arr[ 3 ]; + if (!$parsed["database"]) { + $dsn = preg_replace("|/+$|", "", $dsn); } - elseif( preg_match( "|^([^:]+)@(.*)$|", $dsn, $arr ) ) - { - $parsed[ "username" ] = $arr[ 1 ]; - $dsn = $arr[ 2 ]; - } + $parsed["hostspec"] = urldecode($dsn); - if( preg_match( "|^([^\+]+)\+(.*)$|", $dsn, $arr ) ) - { - $parsed[ "protocol" ] = $arr[ 1 ]; - $dsn = $arr[ 2 ]; + if(!$parsed["dbsyntax"]) { + $parsed["dbsyntax"] = $parsed["phptype"]; } - if( !$parsed[ "database" ] ) - { - $dsn = preg_replace( "|/+$|", "", $dsn ); - } + return $parsed; + } +} - $parsed[ "hostspec" ] = urldecode( $dsn ); +/** + * DB_Error implements a class for reporting portable database error + * messages. + * + * @author Stig Bakken <ssb@fast.no> + */ +class DB_Error extends PEAR_Error +{ + /** + * DB_Error constructor. + * + * @param $code mixed DB error code, or string with error message. + * @param $mode int what "error mode" to operate in + * @param $level what error level to use for $mode & PEAR_ERROR_TRIGGER + * @param $debuginfo additional debug info, such as the last query + * + * @access public + * + * @see PEAR_Error + */ - if( !$parsed[ "dbsyntax"] ) - { - $parsed[ "dbsyntax" ] = $parsed[ "phptype" ]; - } + 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", 0, $mode, $level, $debuginfo); + } + } +} - return $parsed; +/** + * DB_Warning implements a class for reporting portable database + * warning messages. + * + * @author Stig Bakken <ssb@fast.no> + */ +class DB_Warning extends PEAR_Error +{ + /** + * DB_Warning constructor. + * + * @param $code mixed DB error code, or string with error message. + * @param $mode int what "error mode" to operate in + * @param $level what error level to use for $mode == PEAR_ERROR_TRIGGER + * @param $debuginfo additional debug info, such as the last query + * + * @access public + * + * @see PEAR_Error + */ + + function DB_Warning($code = DB_WARNING, $mode = PEAR_ERROR_RETURN, + $level = E_USER_NOTICE, $debuginfo = null) { + if (is_int($code)) { + $this->PEAR_Error("DB Warning: " . DB::errorMessage( $code ), $code, $mode, $level, $debuginfo); + } else { + $this->PEAR_Error("DB Warning: $code", 0, $mode, $level, $debuginfo); } } +} + +/** + * This class implements a wrapper for a DB result set. + * A new instance of this class will be returned by the DB implementation + * after processing a query that returns data. + * + * @author Stig Bakken <ssb@fast.no> + */ + +class DB_result +{ + var $dbh; + var $result; + + /** + * DB_result constructor. + * @param $dbh DB object reference + * @param $result result resource id + */ + + function DB_result(&$dbh, $result) { + $this->dbh = &$dbh; + $this->result = $result; + } + + /** + * Fetch and return a row of data. + * @return array a row of data, or false on error + */ + function fetchRow($fetchmode = DB_FETCHMODE_DEFAULT) { + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->dbh->fetchmode; + } + return $this->dbh->fetchRow($this->result, $fetchmode); + } + + /** + * Fetch a row of data into an existing array. + * + * @param $arr reference to data array + * @return int error code + */ + function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT) { + if ($fetchmode == DB_FETCHMODE_DEFAULT) { + $fetchmode = $this->dbh->fetchmode; + } + return $this->dbh->fetchInto($this->result, $arr, $fetchmode); + } + + /** + * Get the the number of columns in a result set. + * + * @return int the number of columns, or a DB error code + */ + function numCols() { + return $this->dbh->numCols($this->result); + } + + /** + * Frees the resources allocated for this result set. + * @return int error code + */ + function free() { + $err = $this->dbh->freeResult($this->result); + if( DB::isError($err)) { + return $err; + } + $this->result = false; + return true; + } +} + ?>