package.xml0000660000175100017510000002674111626162707012775 0ustar danielcdanielc DB pear.php.net Database Abstraction Layer DB is a database abstraction layer providing: * an OO-style query API * portability features that make programs written for one DBMS work with other DBMS's * a DSN (data source name) format for specifying database servers * prepare/execute (bind) emulation for databases that don't support it natively * a result object for each query response * portable error codes * sequence emulation * sequential and non-sequential row fetching as well as bulk fetching * formats fetched rows as associative arrays, ordered arrays or objects * row limit support * transactions support * table information interface * DocBook and phpDocumentor API documentation DB layers itself on top of PHP's existing database extensions. Drivers for the following extensions pass the complete test suite and provide interchangeability when all of DB's portability options are enabled: fbsql, ibase, informix, msql, mssql, mysql, mysqli, oci8, odbc, pgsql, sqlite and sybase. There is also a driver for the dbase extension, but it can't be used interchangeably because dbase doesn't support many standard DBMS features. DB is compatible with both PHP 4 and PHP 5. Daniel Convissor danielc danielc@php.net yes Adam Harvey aharvey aharvey@php.net yes Stig Bakken ssb stig@php.net no Tomas V.V.Cox cox cox@idecnet.com no 2011-08-27 1.7.14 1.7.14 stable stable PHP License Changes since 1.7.13: All drivers: * Made the definition of DB_common::raiseError() compatible with PEAR::raiseError() by adding dummy parameters. Request 11581. * Fixed SKIP block in the 20locale.phpt test. * Fixed non-persistent connections to really be non-persistent when unserialising. Bug 15115. ibase: * Changed the order of regular expressions applied when mapping errors so that constraint violations in PHP 4.4 are actually reported as constraint violations and not table not found errors. * Fixed a test that should have been skipped in PHP 4.4. ifx: * Use PCRE rather than ereg. Bug 17722, patch by Olle Jonsson. mssql: * Fix escaping of string values with lines ending in a backslash character. Bug 16117. oci8: * Fix for last_query reconstruction with lots of placeholders. Bug 12418, patch by Nicholas Evans. * Fix replacement of values in last_query for :bind's numbered over 10. Bug 14603. * Unset $_prepared_queries in freePrepared(). Bug 14271. pgsql: * Added special-case to handle SAVEPOINT queries as manip queries. Bug 12260. * Implement rudimentary support for WITH...SELECT queries. Bug 17136. sqlite: * Added support for another error message in the SQLite driver. Bug 12105, patch by Adam Ashley. * Implement auto_increment support in tableInfo(). Request 13193. 4.2.0 1.4.0b1 PEAR pear.php.net 1.0b1 DB-1.7.14/DB/common.php0000660000175100017510000021557411626162707014165 0ustar danielcdanielc * @author Tomas V.V. Cox * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: common.php 315557 2011-08-26 14:32:35Z danielc $ * @link http://pear.php.net/package/DB */ /** * Obtain the PEAR class so it can be extended from */ require_once 'PEAR.php'; /** * DB_common is the base class from which each database driver class extends * * All common methods are declared here. If a given DBMS driver contains * a particular method, that method will overload the one here. * * @category Database * @package DB * @author Stig Bakken * @author Tomas V.V. Cox * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_common extends PEAR { // {{{ properties /** * The current default fetch mode * @var integer */ var $fetchmode = DB_FETCHMODE_ORDERED; /** * The name of the class into which results should be fetched when * DB_FETCHMODE_OBJECT is in effect * * @var string */ var $fetchmode_object_class = 'stdClass'; /** * Was a connection present when the object was serialized()? * @var bool * @see DB_common::__sleep(), DB_common::__wake() */ var $was_connected = null; /** * The most recently executed query * @var string */ var $last_query = ''; /** * Run-time configuration options * * The 'optimize' option has been deprecated. Use the 'portability' * option instead. * * @var array * @see DB_common::setOption() */ var $options = array( 'result_buffering' => 500, 'persistent' => false, 'ssl' => false, 'debug' => 0, 'seqname_format' => '%s_seq', 'autofree' => false, 'portability' => DB_PORTABILITY_NONE, 'optimize' => 'performance', // Deprecated. Use 'portability'. ); /** * The parameters from the most recently executed query * @var array * @since Property available since Release 1.7.0 */ var $last_parameters = array(); /** * The elements from each prepared statement * @var array */ var $prepare_tokens = array(); /** * The data types of the various elements in each prepared statement * @var array */ var $prepare_types = array(); /** * The prepared queries * @var array */ var $prepared_queries = array(); /** * Flag indicating that the last query was a manipulation query. * @access protected * @var boolean */ var $_last_query_manip = false; /** * Flag indicating that the next query must be a manipulation * query. * @access protected * @var boolean */ var $_next_query_manip = false; // }}} // {{{ DB_common /** * This constructor calls $this->PEAR('DB_Error') * * @return void */ function DB_common() { $this->PEAR('DB_Error'); } // }}} // {{{ __sleep() /** * Automatically indicates which properties should be saved * when PHP's serialize() function is called * * @return array the array of properties names that should be saved */ function __sleep() { if ($this->connection) { // Don't disconnect(), people use serialize() for many reasons $this->was_connected = true; } else { $this->was_connected = false; } if (isset($this->autocommit)) { return array('autocommit', 'dbsyntax', 'dsn', 'features', 'fetchmode', 'fetchmode_object_class', 'options', 'was_connected', ); } else { return array('dbsyntax', 'dsn', 'features', 'fetchmode', 'fetchmode_object_class', 'options', 'was_connected', ); } } // }}} // {{{ __wakeup() /** * Automatically reconnects to the database when PHP's unserialize() * function is called * * The reconnection attempt is only performed if the object was connected * at the time PHP's serialize() function was run. * * @return void */ function __wakeup() { if ($this->was_connected) { $this->connect($this->dsn, $this->options['persistent']); } } // }}} // {{{ __toString() /** * Automatic string conversion for PHP 5 * * @return string a string describing the current PEAR DB object * * @since Method available since Release 1.7.0 */ function __toString() { $info = strtolower(get_class($this)); $info .= ': (phptype=' . $this->phptype . ', dbsyntax=' . $this->dbsyntax . ')'; if ($this->connection) { $info .= ' [connected]'; } return $info; } // }}} // {{{ toString() /** * DEPRECATED: String conversion method * * @return string a string describing the current PEAR DB object * * @deprecated Method deprecated in Release 1.7.0 */ function toString() { return $this->__toString(); } // }}} // {{{ quoteString() /** * DEPRECATED: Quotes a string so it can be safely used within string * delimiters in a query * * @param string $string the string to be quoted * * @return string the quoted string * * @see DB_common::quoteSmart(), DB_common::escapeSimple() * @deprecated Method deprecated some time before Release 1.2 */ function quoteString($string) { $string = $this->quote($string); if ($string{0} == "'") { return substr($string, 1, -1); } return $string; } // }}} // {{{ quote() /** * DEPRECATED: Quotes a string so it can be safely used in a query * * @param string $string the string to quote * * @return string the quoted string or the string NULL * if the value submitted is null. * * @see DB_common::quoteSmart(), DB_common::escapeSimple() * @deprecated Deprecated in release 1.6.0 */ function quote($string = null) { return ($string === null) ? 'NULL' : "'" . str_replace("'", "''", $string) . "'"; } // }}} // {{{ quoteIdentifier() /** * Quotes a string so it can be safely used as a table or column name * * Delimiting style depends on which database driver is being used. * * NOTE: just because you CAN use delimited identifiers doesn't mean * you SHOULD use them. In general, they end up causing way more * problems than they solve. * * Portability is broken by using the following characters inside * delimited identifiers: * + backtick (`) -- due to MySQL * + double quote (") -- due to Oracle * + brackets ([ or ]) -- due to Access * * Delimited identifiers are known to generally work correctly under * the following drivers: * + mssql * + mysql * + mysqli * + oci8 * + odbc(access) * + odbc(db2) * + pgsql * + sqlite * + sybase (must execute set quoted_identifier on sometime * prior to use) * * InterBase doesn't seem to be able to use delimited identifiers * via PHP 4. They work fine under PHP 5. * * @param string $str the identifier name to be quoted * * @return string the quoted identifier * * @since Method available since Release 1.6.0 */ function quoteIdentifier($str) { return '"' . str_replace('"', '""', $str) . '"'; } // }}} // {{{ quoteSmart() /** * Formats input so it can be safely used in a query * * The output depends on the PHP data type of input and the database * type being used. * * @param mixed $in the data to be formatted * * @return mixed the formatted data. The format depends on the input's * PHP type: *
    *
  • * input -> returns *
  • *
  • * null -> the string NULL *
  • *
  • * integer or double -> the unquoted number *
  • *
  • * bool -> output depends on the driver in use * Most drivers return integers: 1 if * true or 0 if * false. * Some return strings: TRUE if * true or FALSE if * false. * Finally one returns strings: T if * true or F if * false. Here is a list of each DBMS, * the values returned and the suggested column type: *
      *
    • * dbase -> T/F * (Logical) *
    • *
    • * fbase -> TRUE/FALSE * (BOOLEAN) *
    • *
    • * ibase -> 1/0 * (SMALLINT) [1] *
    • *
    • * ifx -> 1/0 * (SMALLINT) [1] *
    • *
    • * msql -> 1/0 * (INTEGER) *
    • *
    • * mssql -> 1/0 * (BIT) *
    • *
    • * mysql -> 1/0 * (TINYINT(1)) *
    • *
    • * mysqli -> 1/0 * (TINYINT(1)) *
    • *
    • * oci8 -> 1/0 * (NUMBER(1)) *
    • *
    • * odbc -> 1/0 * (SMALLINT) [1] *
    • *
    • * pgsql -> TRUE/FALSE * (BOOLEAN) *
    • *
    • * sqlite -> 1/0 * (INTEGER) *
    • *
    • * sybase -> 1/0 * (TINYINT(1)) *
    • *
    * [1] Accommodate the lowest common denominator because not all * versions of have BOOLEAN. *
  • *
  • * other (including strings and numeric strings) -> * the data with single quotes escaped by preceeding * single quotes, backslashes are escaped by preceeding * backslashes, then the whole string is encapsulated * between single quotes *
  • *
* * @see DB_common::escapeSimple() * @since Method available since Release 1.6.0 */ function quoteSmart($in) { if (is_int($in)) { return $in; } elseif (is_float($in)) { return $this->quoteFloat($in); } elseif (is_bool($in)) { return $this->quoteBoolean($in); } elseif (is_null($in)) { return 'NULL'; } else { if ($this->dbsyntax == 'access' && preg_match('/^#.+#$/', $in)) { return $this->escapeSimple($in); } return "'" . $this->escapeSimple($in) . "'"; } } // }}} // {{{ quoteBoolean() /** * Formats a boolean value for use within a query in a locale-independent * manner. * * @param boolean the boolean value to be quoted. * @return string the quoted string. * @see DB_common::quoteSmart() * @since Method available since release 1.7.8. */ function quoteBoolean($boolean) { return $boolean ? '1' : '0'; } // }}} // {{{ quoteFloat() /** * Formats a float value for use within a query in a locale-independent * manner. * * @param float the float value to be quoted. * @return string the quoted string. * @see DB_common::quoteSmart() * @since Method available since release 1.7.8. */ function quoteFloat($float) { return "'".$this->escapeSimple(str_replace(',', '.', strval(floatval($float))))."'"; } // }}} // {{{ escapeSimple() /** * Escapes a string according to the current DBMS's standards * * In SQLite, this makes things safe for inserts/updates, but may * cause problems when performing text comparisons against columns * containing binary data. See the * {@link http://php.net/sqlite_escape_string PHP manual} for more info. * * @param string $str the string to be escaped * * @return string the escaped string * * @see DB_common::quoteSmart() * @since Method available since Release 1.6.0 */ function escapeSimple($str) { return str_replace("'", "''", $str); } // }}} // {{{ provides() /** * Tells whether the present driver supports a given feature * * @param string $feature the feature you're curious about * * @return bool whether this driver supports $feature */ function provides($feature) { return $this->features[$feature]; } // }}} // {{{ setFetchMode() /** * Sets the fetch mode that should be used by default for query results * * @param integer $fetchmode DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC * or DB_FETCHMODE_OBJECT * @param string $object_class the class name of the object to be returned * by the fetch methods when the * DB_FETCHMODE_OBJECT mode is selected. * If no class is specified by default a cast * to object from the assoc array row will be * done. There is also the posibility to use * and extend the 'DB_row' class. * * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT */ function setFetchMode($fetchmode, $object_class = 'stdClass') { switch ($fetchmode) { case DB_FETCHMODE_OBJECT: $this->fetchmode_object_class = $object_class; case DB_FETCHMODE_ORDERED: case DB_FETCHMODE_ASSOC: $this->fetchmode = $fetchmode; break; default: return $this->raiseError('invalid fetchmode mode'); } } // }}} // {{{ setOption() /** * Sets run-time configuration options for PEAR DB * * Options, their data types, default values and description: *
    *
  • * autofree boolean = false *
    should results be freed automatically when there are no * more rows? *
  • * result_buffering integer = 500 *
    how many rows of the result set should be buffered? *
    In mysql: mysql_unbuffered_query() is used instead of * mysql_query() if this value is 0. (Release 1.7.0) *
    In oci8: this value is passed to ocisetprefetch(). * (Release 1.7.0) *
  • * debug integer = 0 *
    debug level *
  • * persistent boolean = false *
    should the connection be persistent? *
  • * portability integer = DB_PORTABILITY_NONE *
    portability mode constant (see below) *
  • * seqname_format string = %s_seq *
    the sprintf() format string used on sequence names. This * format is applied to sequence names passed to * createSequence(), nextID() and dropSequence(). *
  • * ssl boolean = false *
    use ssl to connect? *
  • *
* * ----------------------------------------- * * PORTABILITY MODES * * These modes are bitwised, so they can be combined using | * and removed using ^. See the examples section below on how * to do this. * * DB_PORTABILITY_NONE * turn off all portability features * * This mode gets automatically turned on if the deprecated * optimize option gets set to performance. * * * DB_PORTABILITY_LOWERCASE * convert names of tables and fields to lower case when using * get*(), fetch*() and tableInfo() * * This mode gets automatically turned on in the following databases * if the deprecated option optimize gets set to * portability: * + oci8 * * * DB_PORTABILITY_RTRIM * right trim the data output by get*() fetch*() * * * DB_PORTABILITY_DELETE_COUNT * force reporting the number of rows deleted * * Some DBMS's don't count the number of rows deleted when performing * simple DELETE FROM tablename queries. This portability * mode tricks such DBMS's into telling the count by adding * WHERE 1=1 to the end of DELETE queries. * * This mode gets automatically turned on in the following databases * if the deprecated option optimize gets set to * portability: * + fbsql * + mysql * + mysqli * + sqlite * * * DB_PORTABILITY_NUMROWS * enable hack that makes numRows() work in Oracle * * This mode gets automatically turned on in the following databases * if the deprecated option optimize gets set to * portability: * + oci8 * * * DB_PORTABILITY_ERRORS * makes certain error messages in certain drivers compatible * with those from other DBMS's * * + mysql, mysqli: change unique/primary key constraints * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT * * + odbc(access): MS's ODBC driver reports 'no such field' as code * 07001, which means 'too few parameters.' When this option is on * that code gets mapped to DB_ERROR_NOSUCHFIELD. * DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD * * DB_PORTABILITY_NULL_TO_EMPTY * convert null values to empty strings in data output by get*() and * fetch*(). Needed because Oracle considers empty strings to be null, * while most other DBMS's know the difference between empty and null. * * * DB_PORTABILITY_ALL * turn on all portability features * * ----------------------------------------- * * Example 1. Simple setOption() example * * $db->setOption('autofree', true); * * * Example 2. Portability for lowercasing and trimming * * $db->setOption('portability', * DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM); * * * Example 3. All portability options except trimming * * $db->setOption('portability', * DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM); * * * @param string $option option name * @param mixed $value value for the option * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::$options */ function setOption($option, $value) { if (isset($this->options[$option])) { $this->options[$option] = $value; /* * Backwards compatibility check for the deprecated 'optimize' * option. Done here in case settings change after connecting. */ if ($option == 'optimize') { if ($value == 'portability') { switch ($this->phptype) { case 'oci8': $this->options['portability'] = DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_NUMROWS; break; case 'fbsql': case 'mysql': case 'mysqli': case 'sqlite': $this->options['portability'] = DB_PORTABILITY_DELETE_COUNT; break; } } else { $this->options['portability'] = DB_PORTABILITY_NONE; } } return DB_OK; } return $this->raiseError("unknown option $option"); } // }}} // {{{ getOption() /** * Returns the value of an option * * @param string $option the option name you're curious about * * @return mixed the option's value */ function getOption($option) { if (isset($this->options[$option])) { return $this->options[$option]; } return $this->raiseError("unknown option $option"); } // }}} // {{{ prepare() /** * Prepares a query for multiple execution with execute() * * Creates a query that can be run multiple times. Each time it is run, * the placeholders, if any, will be replaced by the contents of * execute()'s $data argument. * * Three types of placeholders can be used: * + ? scalar value (i.e. strings, integers). The system * will automatically quote and escape the data. * + ! value is inserted 'as is' * + & requires a file name. The file's contents get * inserted into the query (i.e. saving binary * data in a db) * * Example 1. * * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); * $data = array( * "John's text", * "'it''s good'", * 'filename.txt' * ); * $res = $db->execute($sth, $data); * * * Use backslashes to escape placeholder characters if you don't want * them to be interpreted as placeholders: *
     *    "UPDATE foo SET col=? WHERE col='over \& under'"
     * 
* * With some database backends, this is emulated. * * {@internal ibase and oci8 have their own prepare() methods.}} * * @param string $query the query to be prepared * * @return mixed DB statement resource on success. A DB_Error object * on failure. * * @see DB_common::execute() */ function prepare($query) { $tokens = preg_split('/((?prepare_tokens[] = &$newtokens; end($this->prepare_tokens); $k = key($this->prepare_tokens); $this->prepare_types[$k] = $types; $this->prepared_queries[$k] = implode(' ', $newtokens); return $k; } // }}} // {{{ autoPrepare() /** * Automaticaly generates an insert or update query and pass it to prepare() * * @param string $table the table name * @param array $table_fields the array of field names * @param int $mode a type of query to make: * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE * @param string $where for update queries: the WHERE clause to * append to the SQL statement. Don't * include the "WHERE" keyword. * * @return resource the query handle * * @uses DB_common::prepare(), DB_common::buildManipSQL() */ function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, $where = false) { $query = $this->buildManipSQL($table, $table_fields, $mode, $where); if (DB::isError($query)) { return $query; } return $this->prepare($query); } // }}} // {{{ autoExecute() /** * Automaticaly generates an insert or update query and call prepare() * and execute() with it * * @param string $table the table name * @param array $fields_values the associative array where $key is a * field name and $value its value * @param int $mode a type of query to make: * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE * @param string $where for update queries: the WHERE clause to * append to the SQL statement. Don't * include the "WHERE" keyword. * * @return mixed a new DB_result object for successful SELECT queries * or DB_OK for successul data manipulation queries. * A DB_Error object on failure. * * @uses DB_common::autoPrepare(), DB_common::execute() */ function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, $where = false) { $sth = $this->autoPrepare($table, array_keys($fields_values), $mode, $where); if (DB::isError($sth)) { return $sth; } $ret = $this->execute($sth, array_values($fields_values)); $this->freePrepared($sth); return $ret; } // }}} // {{{ buildManipSQL() /** * Produces an SQL query string for autoPrepare() * * Example: *
     * buildManipSQL('table_sql', array('field1', 'field2', 'field3'),
     *               DB_AUTOQUERY_INSERT);
     * 
* * That returns * * INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?) * * * NOTES: * - 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 the table name * @param array $table_fields the array of field names * @param int $mode a type of query to make: * DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE * @param string $where for update queries: the WHERE clause to * append to the SQL statement. Don't * include the "WHERE" keyword. * * @return string the sql query for autoPrepare() */ function buildManipSQL($table, $table_fields, $mode, $where = false) { if (count($table_fields) == 0) { return $this->raiseError(DB_ERROR_NEED_MORE_DATA); } $first = true; switch ($mode) { case DB_AUTOQUERY_INSERT: $values = ''; $names = ''; foreach ($table_fields as $value) { if ($first) { $first = false; } else { $names .= ','; $values .= ','; } $names .= $value; $values .= '?'; } return "INSERT INTO $table ($names) VALUES ($values)"; case DB_AUTOQUERY_UPDATE: $set = ''; foreach ($table_fields as $value) { if ($first) { $first = false; } else { $set .= ','; } $set .= "$value = ?"; } $sql = "UPDATE $table SET $set"; if ($where) { $sql .= " WHERE $where"; } return $sql; default: return $this->raiseError(DB_ERROR_SYNTAX); } } // }}} // {{{ execute() /** * Executes a DB statement prepared with prepare() * * Example 1. * * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)'); * $data = array( * "John's text", * "'it''s good'", * 'filename.txt' * ); * $res = $db->execute($sth, $data); * * * @param resource $stmt a DB statement resource returned from prepare() * @param mixed $data array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return mixed a new DB_result object for successful SELECT queries * or DB_OK for successul data manipulation queries. * A DB_Error object on failure. * * {@internal ibase and oci8 have their own execute() methods.}} * * @see DB_common::prepare() */ function &execute($stmt, $data = array()) { $realquery = $this->executeEmulateQuery($stmt, $data); if (DB::isError($realquery)) { return $realquery; } $result = $this->simpleQuery($realquery); if ($result === DB_OK || DB::isError($result)) { return $result; } else { $tmp = new DB_result($this, $result); return $tmp; } } // }}} // {{{ executeEmulateQuery() /** * Emulates executing prepared statements if the DBMS not support them * * @param resource $stmt a DB statement resource returned from execute() * @param mixed $data array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return mixed a string containing the real query run when emulating * prepare/execute. A DB_Error object on failure. * * @access protected * @see DB_common::execute() */ function executeEmulateQuery($stmt, $data = array()) { $stmt = (int)$stmt; $data = (array)$data; $this->last_parameters = $data; if (count($this->prepare_types[$stmt]) != count($data)) { $this->last_query = $this->prepared_queries[$stmt]; return $this->raiseError(DB_ERROR_MISMATCH); } $realquery = $this->prepare_tokens[$stmt][0]; $i = 0; foreach ($data as $value) { if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) { $realquery .= $this->quoteSmart($value); } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) { $fp = @fopen($value, 'rb'); if (!$fp) { return $this->raiseError(DB_ERROR_ACCESS_VIOLATION); } $realquery .= $this->quoteSmart(fread($fp, filesize($value))); fclose($fp); } else { $realquery .= $value; } $realquery .= $this->prepare_tokens[$stmt][++$i]; } return $realquery; } // }}} // {{{ executeMultiple() /** * Performs several execute() calls on the same statement handle * * $data 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 $data numeric array containing the * data to insert into the query * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::prepare(), DB_common::execute() */ function executeMultiple($stmt, $data) { foreach ($data as $value) { $res = $this->execute($stmt, $value); if (DB::isError($res)) { return $res; } } return DB_OK; } // }}} // {{{ freePrepared() /** * Frees the internal resources associated with a prepared query * * @param resource $stmt the prepared statement's PHP resource * @param bool $free_resource should the PHP resource be freed too? * Use false if you need to get data * from the result set later. * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_common::prepare() */ function freePrepared($stmt, $free_resource = true) { $stmt = (int)$stmt; if (isset($this->prepare_tokens[$stmt])) { unset($this->prepare_tokens[$stmt]); unset($this->prepare_types[$stmt]); unset($this->prepared_queries[$stmt]); return true; } return false; } // }}} // {{{ modifyQuery() /** * Changes a query string for various DBMS specific reasons * * It is defined here to ensure all drivers have this method available. * * @param string $query the query string to modify * * @return string the modified query string * * @access protected * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(), * DB_sqlite::modifyQuery() */ function modifyQuery($query) { return $query; } // }}} // {{{ modifyLimitQuery() /** * Adds LIMIT clauses to a query string according to current DBMS standards * * It is defined here to assure that all implementations * have this method defined. * * @param string $query the query to modify * @param int $from the row to start to fetching (0 = the first row) * @param int $count the numbers of rows to fetch * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return string the query string with LIMIT clauses added * * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { return $query; } // }}} // {{{ query() /** * Sends a query to the database server * * The query string can be either a normal statement to be sent directly * to the server OR if $params are passed the query can have * placeholders and it will be passed through prepare() and execute(). * * @param string $query the SQL query or the statement to prepare * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return mixed a new DB_result object for successful SELECT queries * or DB_OK for successul data manipulation queries. * A DB_Error object on failure. * * @see DB_result, DB_common::prepare(), DB_common::execute() */ function &query($query, $params = array()) { if (sizeof($params) > 0) { $sth = $this->prepare($query); if (DB::isError($sth)) { return $sth; } $ret = $this->execute($sth, $params); $this->freePrepared($sth, false); return $ret; } else { $this->last_parameters = array(); $result = $this->simpleQuery($query); if ($result === DB_OK || DB::isError($result)) { return $result; } else { $tmp = new DB_result($this, $result); return $tmp; } } } // }}} // {{{ limitQuery() /** * Generates and executes a LIMIT query * * @param string $query the query * @param intr $from the row to start to fetching (0 = the first row) * @param int $count the numbers of rows to fetch * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return mixed a new DB_result object for successful SELECT queries * or DB_OK for successul data manipulation queries. * A DB_Error object on failure. */ function &limitQuery($query, $from, $count, $params = array()) { $query = $this->modifyLimitQuery($query, $from, $count, $params); if (DB::isError($query)){ return $query; } $result = $this->query($query, $params); if (is_object($result) && is_a($result, 'DB_result')) { $result->setOption('limit_from', $from); $result->setOption('limit_count', $count); } return $result; } // }}} // {{{ getOne() /** * Fetches the first column of the first row from a query result * * Takes care of doing the query and freeing the results when finished. * * @param string $query the SQL query * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return mixed the returned value of the query. * A DB_Error object on failure. */ function &getOne($query, $params = array()) { $params = (array)$params; // modifyLimitQuery() would be nice here, but it causes BC issues if (sizeof($params) > 0) { $sth = $this->prepare($query); if (DB::isError($sth)) { return $sth; } $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { $res = $this->query($query); } if (DB::isError($res)) { return $res; } $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED); $res->free(); if ($err !== DB_OK) { return $err; } return $row[0]; } // }}} // {{{ getRow() /** * Fetches the first row of data returned from a query result * * Takes care of doing the query and freeing the results when finished. * * @param string $query the SQL query * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * @param int $fetchmode the fetch mode to use * * @return array the first row of results as an array. * A DB_Error object on failure. */ function &getRow($query, $params = array(), $fetchmode = DB_FETCHMODE_DEFAULT) { // compat check, the params and fetchmode parameters used to // have the opposite order if (!is_array($params)) { if (is_array($fetchmode)) { if ($params === null) { $tmp = DB_FETCHMODE_DEFAULT; } else { $tmp = $params; } $params = $fetchmode; $fetchmode = $tmp; } elseif ($params !== null) { $fetchmode = $params; $params = array(); } } // modifyLimitQuery() would be nice here, but it causes BC issues if (sizeof($params) > 0) { $sth = $this->prepare($query); if (DB::isError($sth)) { return $sth; } $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { $res = $this->query($query); } if (DB::isError($res)) { return $res; } $err = $res->fetchInto($row, $fetchmode); $res->free(); if ($err !== DB_OK) { return $err; } return $row; } // }}} // {{{ getCol() /** * Fetches a single column from a query result and returns it as an * indexed array * * @param string $query the SQL query * @param mixed $col which column to return (integer [column number, * starting at 0] or string [column name]) * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return array the results as an array. A DB_Error object on failure. * * @see DB_common::query() */ function &getCol($query, $col = 0, $params = array()) { $params = (array)$params; if (sizeof($params) > 0) { $sth = $this->prepare($query); if (DB::isError($sth)) { return $sth; } $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { $res = $this->query($query); } if (DB::isError($res)) { return $res; } $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC; if (!is_array($row = $res->fetchRow($fetchmode))) { $ret = array(); } else { if (!array_key_exists($col, $row)) { $ret = $this->raiseError(DB_ERROR_NOSUCHFIELD); } else { $ret = array($row[$col]); while (is_array($row = $res->fetchRow($fetchmode))) { $ret[] = $row[$col]; } } } $res->free(); if (DB::isError($row)) { $ret = $row; } return $ret; } // }}} // {{{ getAssoc() /** * Fetches an entire query result and returns 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 DB error code is * returned on errors. If the result set contains fewer than two * columns, a DB_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', false, null,
     *          DB_FETCHMODE_ASSOC, 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 bool $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 mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of * items passed must match quantity of * placeholders in query: meaning 1 * placeholder for non-array parameters or * 1 placeholder per array element. * @param int $fetchmode the fetch mode to use * @param bool $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 the associative array containing the query results. * A DB_Error object on failure. */ function &getAssoc($query, $force_array = false, $params = array(), $fetchmode = DB_FETCHMODE_DEFAULT, $group = false) { $params = (array)$params; if (sizeof($params) > 0) { $sth = $this->prepare($query); if (DB::isError($sth)) { return $sth; } $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { $res = $this->query($query); } if (DB::isError($res)) { return $res; } if ($fetchmode == DB_FETCHMODE_DEFAULT) { $fetchmode = $this->fetchmode; } $cols = $res->numCols(); if ($cols < 2) { $tmp = $this->raiseError(DB_ERROR_TRUNCATED); return $tmp; } $results = array(); if ($cols > 2 || $force_array) { // return array values // XXX this part can be optimized if ($fetchmode == DB_FETCHMODE_ASSOC) { while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) { reset($row); $key = current($row); unset($row[key($row)]); if ($group) { $results[$key][] = $row; } else { $results[$key] = $row; } } } elseif ($fetchmode == DB_FETCHMODE_OBJECT) { while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) { $arr = get_object_vars($row); $key = current($arr); if ($group) { $results[$key][] = $row; } else { $results[$key] = $row; } } } else { while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { // we shift away the first element to get // indices running from 0 again $key = array_shift($row); if ($group) { $results[$key][] = $row; } else { $results[$key] = $row; } } } if (DB::isError($row)) { $results = $row; } } else { // return scalar values while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) { if ($group) { $results[$row[0]][] = $row[1]; } else { $results[$row[0]] = $row[1]; } } if (DB::isError($row)) { $results = $row; } } $res->free(); return $results; } // }}} // {{{ getAll() /** * Fetches all of the rows from a query result * * @param string $query the SQL query * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of * items passed must match quantity of * placeholders in query: meaning 1 * placeholder for non-array parameters or * 1 placeholder per array element. * @param int $fetchmode the fetch mode to use: * + DB_FETCHMODE_ORDERED * + DB_FETCHMODE_ASSOC * + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED * + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED * * @return array the nested array. A DB_Error object on failure. */ function &getAll($query, $params = array(), $fetchmode = DB_FETCHMODE_DEFAULT) { // compat check, the params and fetchmode parameters used to // have the opposite order if (!is_array($params)) { if (is_array($fetchmode)) { if ($params === null) { $tmp = DB_FETCHMODE_DEFAULT; } else { $tmp = $params; } $params = $fetchmode; $fetchmode = $tmp; } elseif ($params !== null) { $fetchmode = $params; $params = array(); } } if (sizeof($params) > 0) { $sth = $this->prepare($query); if (DB::isError($sth)) { return $sth; } $res = $this->execute($sth, $params); $this->freePrepared($sth); } else { $res = $this->query($query); } if ($res === DB_OK || DB::isError($res)) { return $res; } $results = array(); while (DB_OK === $res->fetchInto($row, $fetchmode)) { if ($fetchmode & DB_FETCHMODE_FLIPPED) { foreach ($row as $key => $val) { $results[$key][] = $val; } } else { $results[] = $row; } } $res->free(); if (DB::isError($row)) { $tmp = $this->raiseError($row); return $tmp; } return $results; } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ numRows() /** * Determines the number of rows in a query result * * @param resource $result the query result idenifier produced by PHP * * @return int the number of rows. A DB_Error object on failure. */ function numRows($result) { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ getSequenceName() /** * Generates the name used inside the database for a sequence * * The createSequence() docblock contains notes about storing sequence * names. * * @param string $sqn the sequence's public name * * @return string the sequence's name in the backend * * @access protected * @see DB_common::createSequence(), DB_common::dropSequence(), * DB_common::nextID(), DB_common::setOption() */ function getSequenceName($sqn) { return sprintf($this->getOption('seqname_format'), preg_replace('/[^a-z0-9_.]/i', '_', $sqn)); } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::dropSequence(), * DB_common::getSequenceName() */ function nextId($seq_name, $ondemand = true) { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ createSequence() /** * Creates a new sequence * * The name of a given sequence is determined by passing the string * provided in the $seq_name argument through PHP's sprintf() * function using the value from the seqname_format option as * the sprintf()'s format argument. * * seqname_format is set via setOption(). * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_common::nextID() */ function createSequence($seq_name) { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_common::nextID() */ function dropSequence($seq_name) { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ raiseError() /** * Communicates an error and invoke error callbacks, etc * * Basically a wrapper for PEAR::raiseError without the message string. * * @param mixed integer error code, or a PEAR error object (all * other parameters are ignored if this parameter is * an object * @param int error mode, see PEAR_Error docs * @param mixed if error mode is PEAR_ERROR_TRIGGER, this is the * error level (E_USER_NOTICE etc). If error mode is * PEAR_ERROR_CALLBACK, this is the callback function, * either as a function name, or as an array of an * object and method name. For other error modes this * parameter is ignored. * @param string extra debug information. Defaults to the last * query and native error code. * @param mixed native error code, integer or string depending the * backend * @param mixed dummy parameter for E_STRICT compatibility with * PEAR::raiseError * @param mixed dummy parameter for E_STRICT compatibility with * PEAR::raiseError * * @return object the PEAR_Error object * * @see PEAR_Error */ function &raiseError($code = DB_ERROR, $mode = null, $options = null, $userinfo = null, $nativecode = null, $dummy1 = null, $dummy2 = null) { // The error is yet a DB error object if (is_object($code)) { // because we the static PEAR::raiseError, our global // handler should be used if it is set if ($mode === null && !empty($this->_default_error_mode)) { $mode = $this->_default_error_mode; $options = $this->_default_error_options; } $tmp = PEAR::raiseError($code, null, $mode, $options, null, null, true); return $tmp; } if ($userinfo === null) { $userinfo = $this->last_query; } if ($nativecode) { $userinfo .= ' [nativecode=' . trim($nativecode) . ']'; } else { $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']'; } $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'DB_Error', true); return $tmp; } // }}} // {{{ errorNative() /** * Gets the DBMS' native error code produced by the last query * * @return mixed the DBMS' error code. A DB_Error object on failure. */ function errorNative() { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ errorCode() /** * Maps native error codes to DB's portable ones * * Uses the $errorcode_map property defined in each driver. * * @param string|int $nativecode the error code returned by the DBMS * * @return int the portable DB error code. Return DB_ERROR if the * current driver doesn't have a mapping for the * $nativecode submitted. */ function errorCode($nativecode) { if (isset($this->errorcode_map[$nativecode])) { return $this->errorcode_map[$nativecode]; } // Fall back to DB_ERROR if there was no mapping. return DB_ERROR; } // }}} // {{{ errorMessage() /** * Maps a DB error code to a textual message * * @param integer $dbcode the DB error code * * @return string the error message corresponding to the error code * submitted. FALSE if the error code is unknown. * * @see DB::errorMessage() */ function errorMessage($dbcode) { return DB::errorMessage($this->errorcode_map[$dbcode]); } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * The format of the resulting array depends on which $mode * you select. The sample output below is based on this query: *
     *    SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
     *    FROM tblFoo
     *    JOIN tblBar ON tblFoo.fldId = tblBar.fldId
     * 
* *
    *
  • * * null (default) *
         *   [0] => Array (
         *       [table] => tblFoo
         *       [name] => fldId
         *       [type] => int
         *       [len] => 11
         *       [flags] => primary_key not_null
         *   )
         *   [1] => Array (
         *       [table] => tblFoo
         *       [name] => fldPhone
         *       [type] => string
         *       [len] => 20
         *       [flags] =>
         *   )
         *   [2] => Array (
         *       [table] => tblBar
         *       [name] => fldId
         *       [type] => int
         *       [len] => 11
         *       [flags] => primary_key not_null
         *   )
         *   
    * *
  • * * DB_TABLEINFO_ORDER * *

    In addition to the information found in the default output, * a notation of the number of columns is provided by the * num_fields element while the order * element provides an array with the column names as the keys and * their location index number (corresponding to the keys in the * the default output) as the values.

    * *

    If a result set has identical field names, the last one is * used.

    * *
         *   [num_fields] => 3
         *   [order] => Array (
         *       [fldId] => 2
         *       [fldTrans] => 1
         *   )
         *   
    * *
  • * * DB_TABLEINFO_ORDERTABLE * *

    Similar to DB_TABLEINFO_ORDER but adds more * dimensions to the array in which the table names are keys and * the field names are sub-keys. This is helpful for queries that * join tables which have identical field names.

    * *
         *   [num_fields] => 3
         *   [ordertable] => Array (
         *       [tblFoo] => Array (
         *           [fldId] => 0
         *           [fldPhone] => 1
         *       )
         *       [tblBar] => Array (
         *           [fldId] => 2
         *       )
         *   )
         *   
    * *
  • *
* * The flags element contains a space separated list * of extra information about the field. This data is inconsistent * between DBMS's due to the way each DBMS works. * + primary_key * + unique_key * + multiple_key * + not_null * * Most DBMS's only provide the table and flags * elements if $result is a table name. The following DBMS's * provide full information from queries: * + fbsql * + mysql * * If the 'portability' option has DB_PORTABILITY_LOWERCASE * turned on, the names of tables and fields will be lowercased. * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode either unused or one of the tableInfo modes: * DB_TABLEINFO_ORDERTABLE, * DB_TABLEINFO_ORDER or * DB_TABLEINFO_FULL (which does both). * These are bitwise, so the first two can be * combined using |. * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::setOption() */ function tableInfo($result, $mode = null) { /* * If the DB_ class has a tableInfo() method, that one * overrides this one. But, if the driver doesn't have one, * this method runs and tells users about that fact. */ return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ getTables() /** * Lists the tables in the current database * * @return array the list of tables. A DB_Error object on failure. * * @deprecated Method deprecated some time before Release 1.2 */ function getTables() { return $this->getListOf('tables'); } // }}} // {{{ getListOf() /** * Lists internal database information * * @param string $type type of information being sought. * Common items being sought are: * tables, databases, users, views, functions * Each DBMS's has its own capabilities. * * @return array an array listing the items sought. * A DB DB_Error object on failure. */ function getListOf($type) { $sql = $this->getSpecialQuery($type); if ($sql === null) { $this->last_query = ''; return $this->raiseError(DB_ERROR_UNSUPPORTED); } elseif (is_int($sql) || DB::isError($sql)) { // Previous error return $this->raiseError($sql); } elseif (is_array($sql)) { // Already the result return $sql; } // Launch this query return $this->getCol($sql); } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { return $this->raiseError(DB_ERROR_UNSUPPORTED); } // }}} // {{{ nextQueryIsManip() /** * Sets (or unsets) a flag indicating that the next query will be a * manipulation query, regardless of the usual DB::isManip() heuristics. * * @param boolean true to set the flag overriding the isManip() behaviour, * false to clear it and fall back onto isManip() * * @return void * * @access public */ function nextQueryIsManip($manip) { $this->_next_query_manip = $manip; } // }}} // {{{ _checkManip() /** * Checks if the given query is a manipulation query. This also takes into * account the _next_query_manip flag and sets the _last_query_manip flag * (and resets _next_query_manip) according to the result. * * @param string The query to check. * * @return boolean true if the query is a manipulation query, false * otherwise * * @access protected */ function _checkManip($query) { if ($this->_next_query_manip || DB::isManip($query)) { $this->_last_query_manip = true; } else { $this->_last_query_manip = false; } $this->_next_query_manip = false; return $this->_last_query_manip; $manip = $this->_next_query_manip; } // }}} // {{{ _rtrimArrayValues() /** * Right-trims all strings in an array * * @param array $array the array to be trimmed (passed by reference) * * @return void * * @access protected */ function _rtrimArrayValues(&$array) { foreach ($array as $key => $value) { if (is_string($value)) { $array[$key] = rtrim($value); } } } // }}} // {{{ _convertNullArrayValuesToEmpty() /** * Converts all null values in an array to empty strings * * @param array $array the array to be de-nullified (passed by reference) * * @return void * * @access protected */ function _convertNullArrayValuesToEmpty(&$array) { foreach ($array as $key => $value) { if (is_null($value)) { $array[$key] = ''; } } } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/dbase.php0000660000175100017510000003605411626162707013745 0ustar danielcdanielc * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: dbase.php 242771 2007-09-21 13:40:42Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's dbase extension * for interacting with dBase databases * * These methods overload the ones declared in DB_common. * * @category Database * @package DB * @author Tomas V.V. Cox * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_dbase extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'dbase'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'dbase'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => false, 'new_link' => false, 'numrows' => true, 'pconnect' => false, 'prepare' => false, 'ssl' => false, 'transactions' => false, ); /** * A mapping of native error codes to DB error codes * @var array */ var $errorcode_map = array( ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * A means of emulating result resources * @var array */ var $res_row = array(); /** * The quantity of results so far * * For emulating result resources. * * @var integer */ var $result = 0; /** * Maps dbase data type id's to human readable strings * * The human readable values are based on the output of PHP's * dbase_get_header_info() function. * * @var array * @since Property available since Release 1.7.0 */ var $types = array( 'C' => 'character', 'D' => 'date', 'L' => 'boolean', 'M' => 'memo', 'N' => 'number', ); // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_dbase() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database and create it if it doesn't exist * * Don't call this method directly. Use DB::connect() instead. * * PEAR DB's dbase driver supports the following extra DSN options: * + mode An integer specifying the read/write mode to use * (0 = read only, 1 = write only, 2 = read/write). * Available since PEAR DB 1.7.0. * + fields An array of arrays that PHP's dbase_create() function needs * to create a new database. This information is used if the * dBase file specified in the "database" segment of the DSN * does not exist. For more info, see the PHP manual's * {@link http://php.net/dbase_create dbase_create()} page. * Available since PEAR DB 1.7.0. * * Example of how to connect and establish a new dBase file if necessary: * * require_once 'DB.php'; * * $dsn = array( * 'phptype' => 'dbase', * 'database' => '/path/and/name/of/dbase/file', * 'mode' => 2, * 'fields' => array( * array('a', 'N', 5, 0), * array('b', 'C', 40), * array('c', 'C', 255), * array('d', 'C', 20), * ), * ); * $options = array( * 'debug' => 2, * 'portability' => DB_PORTABILITY_ALL, * ); * * $db = DB::connect($dsn, $options); * if (PEAR::isError($db)) { * die($db->getMessage()); * } * * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('dbase')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } /* * Turn track_errors on for entire script since $php_errormsg * is the only way to find errors from the dbase extension. */ @ini_set('track_errors', 1); $php_errormsg = ''; if (!file_exists($dsn['database'])) { $this->dsn['mode'] = 2; if (empty($dsn['fields']) || !is_array($dsn['fields'])) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, 'the dbase file does not exist and ' . 'it could not be created because ' . 'the "fields" element of the DSN ' . 'is not properly set'); } $this->connection = @dbase_create($dsn['database'], $dsn['fields']); if (!$this->connection) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, 'the dbase file does not exist and ' . 'the attempt to create it failed: ' . $php_errormsg); } } else { if (!isset($this->dsn['mode'])) { $this->dsn['mode'] = 0; } $this->connection = @dbase_open($dsn['database'], $this->dsn['mode']); if (!$this->connection) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $php_errormsg); } } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @dbase_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ &query() function &query($query = null) { // emulate result resources $this->res_row[(int)$this->result] = 0; $tmp = new DB_result($this, $this->result++); return $tmp; } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum === null) { $rownum = $this->res_row[(int)$result]++; } if ($fetchmode & DB_FETCHMODE_ASSOC) { $arr = @dbase_get_record_with_names($this->connection, $rownum); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $arr = @dbase_get_record($this->connection, $rownum); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set. * * This method is a no-op for dbase, as there aren't result resources in * the same sense as most other database backends. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return true; } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($foo) { return @dbase_numfields($this->connection); } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($foo) { return @dbase_numrecords($this->connection); } // }}} // {{{ quoteBoolean() /** * Formats a boolean value for use within a query in a locale-independent * manner. * * @param boolean the boolean value to be quoted. * @return string the quoted string. * @see DB_common::quoteSmart() * @since Method available since release 1.7.8. */ function quoteBoolean($boolean) { return $boolean ? 'T' : 'F'; } // }}} // {{{ tableInfo() /** * Returns information about the current database * * @param mixed $result THIS IS UNUSED IN DBASE. The current database * is examined regardless of what is provided here. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() * @since Method available since Release 1.7.0 */ function tableInfo($result = null, $mode = null) { if (function_exists('dbase_get_header_info')) { $id = @dbase_get_header_info($this->connection); if (!$id && $php_errormsg) { return $this->raiseError(DB_ERROR, null, null, null, $php_errormsg); } } else { /* * This segment for PHP 4 is loosely based on code by * Hadi Rusiah in the comments on * the dBase reference page in the PHP manual. */ $db = @fopen($this->dsn['database'], 'r'); if (!$db) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $php_errormsg); } $id = array(); $i = 0; $line = fread($db, 32); while (!feof($db)) { $line = fread($db, 32); if (substr($line, 0, 1) == chr(13)) { break; } else { $pos = strpos(substr($line, 0, 10), chr(0)); $pos = ($pos == 0 ? 10 : $pos); $id[$i] = array( 'name' => substr($line, 0, $pos), 'type' => $this->types[substr($line, 11, 1)], 'length' => ord(substr($line, 16, 1)), 'precision' => ord(substr($line, 17, 1)), ); } $i++; } fclose($db); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $res = array(); $count = count($id); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $res[$i] = array( 'table' => $this->dsn['database'], 'name' => $case_func($id[$i]['name']), 'type' => $id[$i]['type'], 'len' => $id[$i]['length'], 'flags' => '' ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } return $res; } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/fbsql.php0000660000175100017510000005534111626162707013776 0ustar danielcdanielc * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: fbsql.php 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's fbsql extension * for interacting with FrontBase databases * * These methods overload the ones declared in DB_common. * * @category Database * @package DB * @author Frank M. Kromann * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB * @since Class functional since Release 1.7.0 */ class DB_fbsql extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'fbsql'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'fbsql'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'alter', 'new_link' => false, 'numrows' => true, 'pconnect' => true, 'prepare' => false, 'ssl' => false, 'transactions' => true, ); /** * A mapping of native error codes to DB error codes * @var array */ var $errorcode_map = array( 22 => DB_ERROR_SYNTAX, 85 => DB_ERROR_ALREADY_EXISTS, 108 => DB_ERROR_SYNTAX, 116 => DB_ERROR_NOSUCHTABLE, 124 => DB_ERROR_VALUE_COUNT_ON_ROW, 215 => DB_ERROR_NOSUCHFIELD, 217 => DB_ERROR_INVALID_NUMBER, 226 => DB_ERROR_NOSUCHFIELD, 231 => DB_ERROR_INVALID, 239 => DB_ERROR_TRUNCATED, 251 => DB_ERROR_SYNTAX, 266 => DB_ERROR_NOT_FOUND, 357 => DB_ERROR_CONSTRAINT_NOT_NULL, 358 => DB_ERROR_CONSTRAINT, 360 => DB_ERROR_CONSTRAINT, 361 => DB_ERROR_CONSTRAINT, ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_fbsql() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('fbsql')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } $params = array( $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', $dsn['username'] ? $dsn['username'] : null, $dsn['password'] ? $dsn['password'] : null, ); $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect'; $ini = ini_get('track_errors'); $php_errormsg = ''; if ($ini) { $this->connection = @call_user_func_array($connect_function, $params); } else { @ini_set('track_errors', 1); $this->connection = @call_user_func_array($connect_function, $params); @ini_set('track_errors', $ini); } if (!$this->connection) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $php_errormsg); } if ($dsn['database']) { if (!@fbsql_select_db($dsn['database'], $this->connection)) { return $this->fbsqlRaiseError(); } } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @fbsql_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $this->last_query = $query; $query = $this->modifyQuery($query); $result = @fbsql_query("$query;", $this->connection); if (!$result) { return $this->fbsqlRaiseError(); } // Determine which queries that should return data, and which // should return an error code only. if ($this->_checkManip($query)) { return DB_OK; } return $result; } // }}} // {{{ nextResult() /** * Move the internal fbsql result pointer to the next available result * * @param a valid fbsql result resource * * @access public * * @return true if a result is available otherwise return false */ function nextResult($result) { return @fbsql_next_result($result); } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@fbsql_data_seek($result, $rownum)) { return null; } } if ($fetchmode & DB_FETCHMODE_ASSOC) { $arr = @fbsql_fetch_array($result, FBSQL_ASSOC); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $arr = @fbsql_fetch_row($result); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? fbsql_free_result($result) : false; } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff=false) { if ($onoff) { $this->query("SET COMMIT TRUE"); } else { $this->query("SET COMMIT FALSE"); } } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { @fbsql_commit($this->connection); } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { @fbsql_rollback($this->connection); } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @fbsql_num_fields($result); if (!$cols) { return $this->fbsqlRaiseError(); } return $cols; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($result) { $rows = @fbsql_num_rows($result); if ($rows === null) { return $this->fbsqlRaiseError(); } return $rows; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if ($this->_last_query_manip) { $result = @fbsql_affected_rows($this->connection); } else { $result = 0; } return $result; } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_fbsql::createSequence(), DB_fbsql::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); do { $repeat = 0; $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query('SELECT UNIQUE FROM ' . $seqname); $this->popErrorHandling(); 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 $this->fbsqlRaiseError(); } $result->fetchInto($tmp, DB_FETCHMODE_ORDERED); return $tmp[0]; } /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_fbsql::nextID(), DB_fbsql::dropSequence() */ function createSequence($seq_name) { $seqname = $this->getSequenceName($seq_name); $res = $this->query('CREATE TABLE ' . $seqname . ' (id INTEGER NOT NULL,' . ' PRIMARY KEY(id))'); if ($res) { $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname); } return $res; } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_fbsql::nextID(), DB_fbsql::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name) . ' RESTRICT'); } // }}} // {{{ modifyLimitQuery() /** * Adds LIMIT clauses to a query string according to current DBMS standards * * @param string $query the query to modify * @param int $from the row to start to fetching (0 = the first row) * @param int $count the numbers of rows to fetch * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return string the query string with LIMIT clauses added * * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { if (DB::isManip($query) || $this->_next_query_manip) { return preg_replace('/^([\s(])*SELECT/i', "\\1SELECT TOP($count)", $query); } else { return preg_replace('/([\s(])*SELECT/i', "\\1SELECT TOP($from, $count)", $query); } } // }}} // {{{ quoteBoolean() /** * Formats a boolean value for use within a query in a locale-independent * manner. * * @param boolean the boolean value to be quoted. * @return string the quoted string. * @see DB_common::quoteSmart() * @since Method available since release 1.7.8. */ function quoteBoolean($boolean) { return $boolean ? 'TRUE' : 'FALSE'; } // }}} // {{{ quoteFloat() /** * Formats a float value for use within a query in a locale-independent * manner. * * @param float the float value to be quoted. * @return string the quoted string. * @see DB_common::quoteSmart() * @since Method available since release 1.7.8. */ function quoteFloat($float) { return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); } // }}} // {{{ fbsqlRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_fbsql::errorNative(), DB_common::errorCode() */ function fbsqlRaiseError($errno = null) { if ($errno === null) { $errno = $this->errorCode(fbsql_errno($this->connection)); } return $this->raiseError($errno, null, null, null, @fbsql_error($this->connection)); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error code produced by the last query * * @return int the DBMS' error code */ function errorNative() { return @fbsql_errno($this->connection); } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) { if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ $id = @fbsql_list_fields($this->dsn['database'], $result, $this->connection); $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ $id = $result; $got_string = false; } if (!is_resource($id)) { return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = @fbsql_num_fields($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $res[$i] = array( 'table' => $case_func(@fbsql_field_table($id, $i)), 'name' => $case_func(@fbsql_field_name($id, $i)), 'type' => @fbsql_field_type($id, $i), 'len' => @fbsql_field_len($id, $i), 'flags' => @fbsql_field_flags($id, $i), ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } // free the result only if we were called on a table if ($got_string) { @fbsql_free_result($id); } return $res; } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return 'SELECT "table_name" FROM information_schema.tables' . ' t0, information_schema.schemata t1' . ' WHERE t0.schema_pk=t1.schema_pk AND' . ' "table_type" = \'BASE TABLE\'' . ' AND "schema_name" = current_schema'; case 'views': return 'SELECT "table_name" FROM information_schema.tables' . ' t0, information_schema.schemata t1' . ' WHERE t0.schema_pk=t1.schema_pk AND' . ' "table_type" = \'VIEW\'' . ' AND "schema_name" = current_schema'; case 'users': return 'SELECT "user_name" from information_schema.users'; case 'functions': return 'SELECT "routine_name" FROM' . ' information_schema.psm_routines' . ' t0, information_schema.schemata t1' . ' WHERE t0.schema_pk=t1.schema_pk' . ' AND "routine_kind"=\'FUNCTION\'' . ' AND "schema_name" = current_schema'; case 'procedures': return 'SELECT "routine_name" FROM' . ' information_schema.psm_routines' . ' t0, information_schema.schemata t1' . ' WHERE t0.schema_pk=t1.schema_pk' . ' AND "routine_kind"=\'PROCEDURE\'' . ' AND "schema_name" = current_schema'; default: return null; } } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/ibase.php0000660000175100017510000010250411626162707013744 0ustar danielcdanielc * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: ibase.php 277804 2009-03-26 07:16:31Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's interbase extension * for interacting with Interbase and Firebird databases * * These methods overload the ones declared in DB_common. * * While this class works with PHP 4, PHP's InterBase extension is * unstable in PHP 4. Use PHP 5. * * NOTICE: limitQuery() only works for Firebird. * * @category Database * @package DB * @author Sterling Hughes * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB * @since Class became stable in Release 1.7.0 */ class DB_ibase extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'ibase'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'ibase'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * NOTE: only firebird supports limit. * * @var array */ var $features = array( 'limit' => false, 'new_link' => false, 'numrows' => 'emulate', 'pconnect' => true, 'prepare' => true, 'ssl' => false, 'transactions' => true, ); /** * A mapping of native error codes to DB error codes * @var array */ var $errorcode_map = array( -104 => DB_ERROR_SYNTAX, -150 => DB_ERROR_ACCESS_VIOLATION, -151 => DB_ERROR_ACCESS_VIOLATION, -155 => DB_ERROR_NOSUCHTABLE, -157 => DB_ERROR_NOSUCHFIELD, -158 => DB_ERROR_VALUE_COUNT_ON_ROW, -170 => DB_ERROR_MISMATCH, -171 => DB_ERROR_MISMATCH, -172 => DB_ERROR_INVALID, // -204 => // Covers too many errors, need to use regex on msg -205 => DB_ERROR_NOSUCHFIELD, -206 => DB_ERROR_NOSUCHFIELD, -208 => DB_ERROR_INVALID, -219 => DB_ERROR_NOSUCHTABLE, -297 => DB_ERROR_CONSTRAINT, -303 => DB_ERROR_INVALID, -413 => DB_ERROR_INVALID_NUMBER, -530 => DB_ERROR_CONSTRAINT, -551 => DB_ERROR_ACCESS_VIOLATION, -552 => DB_ERROR_ACCESS_VIOLATION, // -607 => // Covers too many errors, need to use regex on msg -625 => DB_ERROR_CONSTRAINT_NOT_NULL, -803 => DB_ERROR_CONSTRAINT, -804 => DB_ERROR_VALUE_COUNT_ON_ROW, // -902 => // Covers too many errors, need to use regex on msg -904 => DB_ERROR_CONNECT_FAILED, -922 => DB_ERROR_NOSUCHDB, -923 => DB_ERROR_CONNECT_FAILED, -924 => DB_ERROR_CONNECT_FAILED ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * The number of rows affected by a data manipulation query * @var integer * @access private */ var $affected = 0; /** * Should data manipulation queries be committed automatically? * @var bool * @access private */ var $autocommit = true; /** * The prepared statement handle from the most recently executed statement * * {@internal Mainly here because the InterBase/Firebird API is only * able to retrieve data from result sets if the statemnt handle is * still in scope.}} * * @var resource */ var $last_stmt; /** * Is the given prepared statement a data manipulation query? * @var array * @access private */ var $manip_query = array(); // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_ibase() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * PEAR DB's ibase driver supports the following extra DSN options: * + buffers The number of database buffers to allocate for the * server-side cache. * + charset The default character set for a database. * + dialect The default SQL dialect for any statement * executed within a connection. Defaults to the * highest one supported by client libraries. * Functional only with InterBase 6 and up. * + role Functional only with InterBase 5 and up. * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('interbase')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } if ($this->dbsyntax == 'firebird') { $this->features['limit'] = 'alter'; } $params = array( $dsn['hostspec'] ? ($dsn['hostspec'] . ':' . $dsn['database']) : $dsn['database'], $dsn['username'] ? $dsn['username'] : null, $dsn['password'] ? $dsn['password'] : null, isset($dsn['charset']) ? $dsn['charset'] : null, isset($dsn['buffers']) ? $dsn['buffers'] : null, isset($dsn['dialect']) ? $dsn['dialect'] : null, isset($dsn['role']) ? $dsn['role'] : null, ); $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect'; $this->connection = @call_user_func_array($connect_function, $params); if (!$this->connection) { return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED); } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @ibase_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $ismanip = $this->_checkManip($query); $this->last_query = $query; $query = $this->modifyQuery($query); $result = @ibase_query($this->connection, $query); if (!$result) { return $this->ibaseRaiseError(); } if ($this->autocommit && $ismanip) { @ibase_commit($this->connection); } if ($ismanip) { $this->affected = $result; return DB_OK; } else { $this->affected = 0; return $result; } } // }}} // {{{ modifyLimitQuery() /** * Adds LIMIT clauses to a query string according to current DBMS standards * * Only works with Firebird. * * @param string $query the query to modify * @param int $from the row to start to fetching (0 = the first row) * @param int $count the numbers of rows to fetch * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return string the query string with LIMIT clauses added * * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { if ($this->dsn['dbsyntax'] == 'firebird') { $query = preg_replace('/^([\s(])*SELECT/i', "SELECT FIRST $count SKIP $from", $query); } return $query; } // }}} // {{{ nextResult() /** * Move the internal ibase result pointer to the next available result * * @param a valid fbsql result resource * * @access public * * @return true if a result is available otherwise return false */ function nextResult($result) { return false; } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); } if ($fetchmode & DB_FETCHMODE_ASSOC) { if (function_exists('ibase_fetch_assoc')) { $arr = @ibase_fetch_assoc($result); } else { $arr = get_object_vars(ibase_fetch_object($result)); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $arr = @ibase_fetch_row($result); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? ibase_free_result($result) : false; } // }}} // {{{ freeQuery() function freeQuery($query) { return is_resource($query) ? ibase_free_query($query) : false; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if (is_integer($this->affected)) { return $this->affected; } return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @ibase_num_fields($result); if (!$cols) { return $this->ibaseRaiseError(); } return $cols; } // }}} // {{{ prepare() /** * Prepares a query for multiple execution with execute(). * * prepare() requires a generic query as string like * INSERT INTO numbers VALUES (?, ?, ?) * . The ? characters are placeholders. * * Three types of placeholders can be used: * + ? a quoted scalar value, i.e. strings, integers * + ! value is inserted 'as is' * + & requires a file name. The file's contents get * inserted into the query (i.e. saving binary * data in a db) * * Use backslashes to escape placeholder characters if you don't want * them to be interpreted as placeholders. Example: * "UPDATE foo SET col=? WHERE col='over \& under'" * * * @param string $query query to be prepared * @return mixed DB statement resource on success. DB_Error on failure. */ function prepare($query) { $tokens = preg_split('/((? $val) { switch ($val) { case '?': $types[$token++] = DB_PARAM_SCALAR; break; case '&': $types[$token++] = DB_PARAM_OPAQUE; break; case '!': $types[$token++] = DB_PARAM_MISC; break; default: $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); $newquery .= $tokens[$key] . '?'; } } $newquery = substr($newquery, 0, -1); $this->last_query = $query; $newquery = $this->modifyQuery($newquery); $stmt = @ibase_prepare($this->connection, $newquery); if ($stmt === false) { $stmt = $this->ibaseRaiseError(); } else { $this->prepare_types[(int)$stmt] = $types; $this->manip_query[(int)$stmt] = DB::isManip($query); } return $stmt; } // }}} // {{{ execute() /** * Executes a DB statement prepared with prepare(). * * @param resource $stmt a DB statement resource returned from prepare() * @param mixed $data array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 for non-array items or the * quantity of elements in the array. * @return object a new DB_Result or a DB_Error when fail * @see DB_ibase::prepare() * @access public */ function &execute($stmt, $data = array()) { $data = (array)$data; $this->last_parameters = $data; $types = $this->prepare_types[(int)$stmt]; if (count($types) != count($data)) { $tmp = $this->raiseError(DB_ERROR_MISMATCH); return $tmp; } $i = 0; foreach ($data as $key => $value) { if ($types[$i] == DB_PARAM_MISC) { /* * ibase doesn't seem to have the ability to pass a * parameter along unchanged, so strip off quotes from start * and end, plus turn two single quotes to one single quote, * in order to avoid the quotes getting escaped by * ibase and ending up in the database. */ $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); $data[$key] = str_replace("''", "'", $data[$key]); } elseif ($types[$i] == DB_PARAM_OPAQUE) { $fp = @fopen($data[$key], 'rb'); if (!$fp) { $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION); return $tmp; } $data[$key] = fread($fp, filesize($data[$key])); fclose($fp); } $i++; } array_unshift($data, $stmt); $res = call_user_func_array('ibase_execute', $data); if (!$res) { $tmp = $this->ibaseRaiseError(); return $tmp; } /* XXX need this? if ($this->autocommit && $this->manip_query[(int)$stmt]) { @ibase_commit($this->connection); }*/ $this->last_stmt = $stmt; if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) { $this->_last_query_manip = true; $this->_next_query_manip = false; $tmp = DB_OK; } else { $this->_last_query_manip = false; $tmp = new DB_result($this, $res); } return $tmp; } /** * Frees the internal resources associated with a prepared query * * @param resource $stmt the prepared statement's PHP resource * @param bool $free_resource should the PHP resource be freed too? * Use false if you need to get data * from the result set later. * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_ibase::prepare() */ function freePrepared($stmt, $free_resource = true) { if (!is_resource($stmt)) { return false; } if ($free_resource) { @ibase_free_query($stmt); } unset($this->prepare_tokens[(int)$stmt]); unset($this->prepare_types[(int)$stmt]); unset($this->manip_query[(int)$stmt]); return true; } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { $this->autocommit = $onoff ? 1 : 0; return DB_OK; } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { return @ibase_commit($this->connection); } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { return @ibase_rollback($this->connection); } // }}} // {{{ transactionInit() function transactionInit($trans_args = 0) { return $trans_args ? @ibase_trans($trans_args, $this->connection) : @ibase_trans(); } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_ibase::createSequence(), DB_ibase::dropSequence() */ function nextId($seq_name, $ondemand = true) { $sqn = strtoupper($this->getSequenceName($seq_name)); $repeat = 0; do { $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query("SELECT GEN_ID(${sqn}, 1) " . 'FROM RDB$GENERATORS ' . "WHERE RDB\$GENERATOR_NAME='${sqn}'"); $this->popErrorHandling(); if ($ondemand && DB::isError($result)) { $repeat = 1; $result = $this->createSequence($seq_name); if (DB::isError($result)) { return $result; } } else { $repeat = 0; } } while ($repeat); if (DB::isError($result)) { return $this->raiseError($result); } $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); $result->free(); return $arr[0]; } // }}} // {{{ createSequence() /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_ibase::nextID(), DB_ibase::dropSequence() */ function createSequence($seq_name) { $sqn = strtoupper($this->getSequenceName($seq_name)); $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query("CREATE GENERATOR ${sqn}"); $this->popErrorHandling(); return $result; } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_ibase::nextID(), DB_ibase::createSequence() */ function dropSequence($seq_name) { return $this->query('DELETE FROM RDB$GENERATORS ' . "WHERE RDB\$GENERATOR_NAME='" . strtoupper($this->getSequenceName($seq_name)) . "'"); } // }}} // {{{ _ibaseFieldFlags() /** * Get the column's flags * * Supports "primary_key", "unique_key", "not_null", "default", * "computed" and "blob". * * @param string $field_name the name of the field * @param string $table_name the name of the table * * @return string the flags * * @access private */ function _ibaseFieldFlags($field_name, $table_name) { $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE' .' FROM RDB$INDEX_SEGMENTS I' .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME' .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\'' .' AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''; $result = @ibase_query($this->connection, $sql); if (!$result) { return $this->ibaseRaiseError(); } $flags = ''; if ($obj = @ibase_fetch_object($result)) { @ibase_free_result($result); if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') { $flags .= 'primary_key '; } if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') { $flags .= 'unique_key '; } } $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,' .' R.RDB$DEFAULT_SOURCE AS DSOURCE,' .' F.RDB$FIELD_TYPE AS FTYPE,' .' F.RDB$COMPUTED_SOURCE AS CSOURCE' .' FROM RDB$RELATION_FIELDS R ' .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME' .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'' .' AND R.RDB$FIELD_NAME=\'' . $field_name . '\''; $result = @ibase_query($this->connection, $sql); if (!$result) { return $this->ibaseRaiseError(); } if ($obj = @ibase_fetch_object($result)) { @ibase_free_result($result); if (isset($obj->NFLAG)) { $flags .= 'not_null '; } if (isset($obj->DSOURCE)) { $flags .= 'default '; } if (isset($obj->CSOURCE)) { $flags .= 'computed '; } if (isset($obj->FTYPE) && $obj->FTYPE == 261) { $flags .= 'blob '; } } return trim($flags); } // }}} // {{{ ibaseRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_ibase::errorNative(), DB_ibase::errorCode() */ function &ibaseRaiseError($errno = null) { if ($errno === null) { $errno = $this->errorCode($this->errorNative()); } $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg()); return $tmp; } // }}} // {{{ errorNative() /** * Gets the DBMS' native error code produced by the last query * * @return int the DBMS' error code. NULL if there is no error code. * * @since Method available since Release 1.7.0 */ function errorNative() { if (function_exists('ibase_errcode')) { return @ibase_errcode(); } if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i', @ibase_errmsg(), $m)) { return (int)$m[1]; } return null; } // }}} // {{{ errorCode() /** * Maps native error codes to DB's portable ones * * @param int $nativecode the error code returned by the DBMS * * @return int the portable DB error code. Return DB_ERROR if the * current driver doesn't have a mapping for the * $nativecode submitted. * * @since Method available since Release 1.7.0 */ function errorCode($nativecode = null) { if (isset($this->errorcode_map[$nativecode])) { return $this->errorcode_map[$nativecode]; } static $error_regexps; if (!isset($error_regexps)) { $error_regexps = array( '/generator .* is not defined/' => DB_ERROR_SYNTAX, // for compat. w ibase_errcode() '/violation of [\w ]+ constraint/i' => DB_ERROR_CONSTRAINT, '/table.*(not exist|not found|unknown)/i' => DB_ERROR_NOSUCHTABLE, '/table .* already exists/i' => DB_ERROR_ALREADY_EXISTS, '/unsuccessful metadata update .* failed attempt to store duplicate value/i' => DB_ERROR_ALREADY_EXISTS, '/unsuccessful metadata update .* not found/i' => DB_ERROR_NOT_FOUND, '/validation error for column .* value "\*\*\* null/i' => DB_ERROR_CONSTRAINT_NOT_NULL, '/conversion error from string/i' => DB_ERROR_INVALID_NUMBER, '/no permission for/i' => DB_ERROR_ACCESS_VIOLATION, '/arithmetic exception, numeric overflow, or string truncation/i' => DB_ERROR_INVALID, '/feature is not supported/i' => DB_ERROR_NOT_CAPABLE, ); } $errormsg = @ibase_errmsg(); foreach ($error_regexps as $regexp => $code) { if (preg_match($regexp, $errormsg)) { return $code; } } return DB_ERROR; } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) { if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ $id = @ibase_query($this->connection, "SELECT * FROM $result WHERE 1=0"); $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ $id = $result; $got_string = false; } if (!is_resource($id)) { return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = @ibase_num_fields($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $info = @ibase_field_info($id, $i); $res[$i] = array( 'table' => $got_string ? $case_func($result) : '', 'name' => $case_func($info['name']), 'type' => $info['type'], 'len' => $info['length'], 'flags' => ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : '', ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } // free the result only if we were called on a table if ($got_string) { @ibase_free_result($id); } return $res; } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM ' . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0'; case 'views': return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS'; case 'users': return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES'; default: return null; } } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/ifx.php0000660000175100017510000004713011626162707013452 0ustar danielcdanielc * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: ifx.php 302738 2010-08-24 03:06:52Z clockwerx $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's ifx extension * for interacting with Informix databases * * These methods overload the ones declared in DB_common. * * More info on Informix errors can be found at: * http://www.informix.com/answers/english/ierrors.htm * * TODO: * - set needed env Informix vars on connect * - implement native prepare/execute * * @category Database * @package DB * @author Tomas V.V.Cox * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_ifx extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'ifx'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'ifx'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'emulate', 'new_link' => false, 'numrows' => 'emulate', 'pconnect' => true, 'prepare' => false, 'ssl' => false, 'transactions' => true, ); /** * A mapping of native error codes to DB error codes * @var array */ var $errorcode_map = array( '-201' => DB_ERROR_SYNTAX, '-206' => DB_ERROR_NOSUCHTABLE, '-217' => DB_ERROR_NOSUCHFIELD, '-236' => DB_ERROR_VALUE_COUNT_ON_ROW, '-239' => DB_ERROR_CONSTRAINT, '-253' => DB_ERROR_SYNTAX, '-268' => DB_ERROR_CONSTRAINT, '-292' => DB_ERROR_CONSTRAINT_NOT_NULL, '-310' => DB_ERROR_ALREADY_EXISTS, '-316' => DB_ERROR_ALREADY_EXISTS, '-319' => DB_ERROR_NOT_FOUND, '-329' => DB_ERROR_NODBSELECTED, '-346' => DB_ERROR_CONSTRAINT, '-386' => DB_ERROR_CONSTRAINT_NOT_NULL, '-391' => DB_ERROR_CONSTRAINT_NOT_NULL, '-554' => DB_ERROR_SYNTAX, '-691' => DB_ERROR_CONSTRAINT, '-692' => DB_ERROR_CONSTRAINT, '-703' => DB_ERROR_CONSTRAINT_NOT_NULL, '-1202' => DB_ERROR_DIVZERO, '-1204' => DB_ERROR_INVALID_DATE, '-1205' => DB_ERROR_INVALID_DATE, '-1206' => DB_ERROR_INVALID_DATE, '-1209' => DB_ERROR_INVALID_DATE, '-1210' => DB_ERROR_INVALID_DATE, '-1212' => DB_ERROR_INVALID_DATE, '-1213' => DB_ERROR_INVALID_NUMBER, ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * Should data manipulation queries be committed automatically? * @var bool * @access private */ var $autocommit = true; /** * The quantity of transactions begun * * {@internal While this is private, it can't actually be designated * private in PHP 5 because it is directly accessed in the test suite.}} * * @var integer * @access private */ var $transaction_opcount = 0; /** * The number of rows affected by a data manipulation query * @var integer * @access private */ var $affected = 0; // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_ifx() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('informix') && !PEAR::loadExtension('Informix')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : ''; $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : ''; $user = $dsn['username'] ? $dsn['username'] : ''; $pw = $dsn['password'] ? $dsn['password'] : ''; $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect'; $this->connection = @$connect_function($dbname, $user, $pw); if (!is_resource($this->connection)) { return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED); } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @ifx_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $ismanip = $this->_checkManip($query); $this->last_query = $query; $this->affected = null; if (preg_match('/(SELECT|EXECUTE)/i', $query)) { //TESTME: Use !DB::isManip()? // the scroll is needed for fetching absolute row numbers // in a select query result $result = @ifx_query($query, $this->connection, IFX_SCROLL); } else { if (!$this->autocommit && $ismanip) { if ($this->transaction_opcount == 0) { $result = @ifx_query('BEGIN WORK', $this->connection); if (!$result) { return $this->ifxRaiseError(); } } $this->transaction_opcount++; } $result = @ifx_query($query, $this->connection); } if (!$result) { return $this->ifxRaiseError(); } $this->affected = @ifx_affected_rows($result); // Determine which queries should return data, and which // should return an error code only. if (preg_match('/(SELECT|EXECUTE)/i', $query)) { return $result; } // XXX Testme: free results inside a transaction // may cause to stop it and commit the work? // Result has to be freed even with a insert or update @ifx_free_result($result); return DB_OK; } // }}} // {{{ nextResult() /** * Move the internal ifx result pointer to the next available result * * @param a valid fbsql result resource * * @access public * * @return true if a result is available otherwise return false */ function nextResult($result) { return false; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if ($this->_last_query_manip) { return $this->affected; } else { return 0; } } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if (($rownum !== null) && ($rownum < 0)) { return null; } if ($rownum === null) { /* * Even though fetch_row() should return the next row if * $rownum is null, it doesn't in all cases. Bug 598. */ $rownum = 'NEXT'; } else { // Index starts at row 1, unlike most DBMS's starting at 0. $rownum++; } if (!$arr = @ifx_fetch_row($result, $rownum)) { return null; } if ($fetchmode !== DB_FETCHMODE_ASSOC) { $i=0; $order = array(); foreach ($arr as $val) { $order[$i++] = $val; } $arr = $order; } elseif ($fetchmode == DB_FETCHMODE_ASSOC && $this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $arr = array_change_key_case($arr, CASE_LOWER); } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { if (!$cols = @ifx_num_fields($result)) { return $this->ifxRaiseError(); } return $cols; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? ifx_free_result($result) : false; } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = true) { // XXX if $this->transaction_opcount > 0, we should probably // issue a warning here. $this->autocommit = $onoff ? true : false; return DB_OK; } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { if ($this->transaction_opcount > 0) { $result = @ifx_query('COMMIT WORK', $this->connection); $this->transaction_opcount = 0; if (!$result) { return $this->ifxRaiseError(); } } return DB_OK; } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { if ($this->transaction_opcount > 0) { $result = @ifx_query('ROLLBACK WORK', $this->connection); $this->transaction_opcount = 0; if (!$result) { return $this->ifxRaiseError(); } } return DB_OK; } // }}} // {{{ ifxRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_ifx::errorNative(), DB_ifx::errorCode() */ function ifxRaiseError($errno = null) { if ($errno === null) { $errno = $this->errorCode(ifx_error()); } return $this->raiseError($errno, null, null, null, $this->errorNative()); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error code and message produced by the last query * * @return string the DBMS' error code and message */ function errorNative() { return @ifx_error() . ' ' . @ifx_errormsg(); } // }}} // {{{ errorCode() /** * Maps native error codes to DB's portable ones. * * Requires that the DB implementation's constructor fills * in the $errorcode_map property. * * @param string $nativecode error code returned by the database * @return int a portable DB error code, or DB_ERROR if this DB * implementation has no mapping for the given error code. */ function errorCode($nativecode) { if (preg_match('/SQLCODE=(.*)]/', $nativecode, $match)) { $code = $match[1]; if (isset($this->errorcode_map[$code])) { return $this->errorcode_map[$code]; } } return DB_ERROR; } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * NOTE: only supports 'table' if $result is a table name. * * If analyzing a query result and the result has duplicate field names, * an error will be raised saying * can't distinguish duplicate field names. * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() * @since Method available since Release 1.6.0 */ function tableInfo($result, $mode = null) { if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ $id = @ifx_query("SELECT * FROM $result WHERE 1=0", $this->connection); $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. */ $id = $result; $got_string = false; } if (!is_resource($id)) { return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA); } $flds = @ifx_fieldproperties($id); $count = @ifx_num_fields($id); if (count($flds) != $count) { return $this->raiseError("can't distinguish duplicate field names"); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $i = 0; $res = array(); if ($mode) { $res['num_fields'] = $count; } foreach ($flds as $key => $value) { $props = explode(';', $value); $res[$i] = array( 'table' => $got_string ? $case_func($result) : '', 'name' => $case_func($key), 'type' => $props[0], 'len' => $props[1], 'flags' => $props[4] == 'N' ? 'not_null' : '', ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } $i++; } // free the result only if we were called on a table if ($got_string) { @ifx_free_result($id); } return $res; } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return 'SELECT tabname FROM systables WHERE tabid >= 100'; default: return null; } } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/msql.php0000660000175100017510000006005711626162707013643 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: msql.php 242771 2007-09-21 13:40:42Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's msql extension * for interacting with Mini SQL databases * * These methods overload the ones declared in DB_common. * * PHP's mSQL extension did weird things with NULL values prior to PHP * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds * those versions. * * @category Database * @package DB * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB * @since Class not functional until Release 1.7.0 */ class DB_msql extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'msql'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'msql'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'emulate', 'new_link' => false, 'numrows' => true, 'pconnect' => true, 'prepare' => false, 'ssl' => false, 'transactions' => false, ); /** * A mapping of native error codes to DB error codes * @var array */ var $errorcode_map = array( ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * The query result resource created by PHP * * Used to make affectedRows() work. Only contains the result for * data manipulation queries. Contains false for other queries. * * @var resource * @access private */ var $_result; // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_msql() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * Example of how to connect: * * require_once 'DB.php'; * * // $dsn = 'msql://hostname/dbname'; // use a TCP connection * $dsn = 'msql:///dbname'; // use a socket * $options = array( * 'portability' => DB_PORTABILITY_ALL, * ); * * $db = DB::connect($dsn, $options); * if (PEAR::isError($db)) { * die($db->getMessage()); * } * * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('msql')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } $params = array(); if ($dsn['hostspec']) { $params[] = $dsn['port'] ? $dsn['hostspec'] . ',' . $dsn['port'] : $dsn['hostspec']; } $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect'; $ini = ini_get('track_errors'); $php_errormsg = ''; if ($ini) { $this->connection = @call_user_func_array($connect_function, $params); } else { @ini_set('track_errors', 1); $this->connection = @call_user_func_array($connect_function, $params); @ini_set('track_errors', $ini); } if (!$this->connection) { if (($err = @msql_error()) != '') { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $err); } else { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $php_errormsg); } } if (!@msql_select_db($dsn['database'], $this->connection)) { return $this->msqlRaiseError(); } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @msql_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $this->last_query = $query; $query = $this->modifyQuery($query); $result = @msql_query($query, $this->connection); if (!$result) { return $this->msqlRaiseError(); } // Determine which queries that should return data, and which // should return an error code only. if ($this->_checkManip($query)) { $this->_result = $result; return DB_OK; } else { $this->_result = false; return $result; } } // }}} // {{{ nextResult() /** * Move the internal msql result pointer to the next available result * * @param a valid fbsql result resource * * @access public * * @return true if a result is available otherwise return false */ function nextResult($result) { return false; } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * PHP's mSQL extension did weird things with NULL values prior to PHP * 4.3.11 and 5.0.4. Make sure your version of PHP meets or exceeds * those versions. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@msql_data_seek($result, $rownum)) { return null; } } if ($fetchmode & DB_FETCHMODE_ASSOC) { $arr = @msql_fetch_array($result, MSQL_ASSOC); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $arr = @msql_fetch_row($result); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? msql_free_result($result) : false; } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @msql_num_fields($result); if (!$cols) { return $this->msqlRaiseError(); } return $cols; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($result) { $rows = @msql_num_rows($result); if ($rows === false) { return $this->msqlRaiseError(); } return $rows; } // }}} // {{{ affected() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if (!$this->_result) { return 0; } return msql_affected_rows($this->_result); } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_msql::createSequence(), DB_msql::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); $repeat = false; do { $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query("SELECT _seq FROM ${seqname}"); $this->popErrorHandling(); if ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { $repeat = true; $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->createSequence($seq_name); $this->popErrorHandling(); if (DB::isError($result)) { return $this->raiseError($result); } } else { $repeat = false; } } while ($repeat); if (DB::isError($result)) { return $this->raiseError($result); } $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); $result->free(); return $arr[0]; } // }}} // {{{ createSequence() /** * Creates a new sequence * * Also creates a new table to associate the sequence with. Uses * a separate table to ensure portability with other drivers. * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_msql::nextID(), DB_msql::dropSequence() */ function createSequence($seq_name) { $seqname = $this->getSequenceName($seq_name); $res = $this->query('CREATE TABLE ' . $seqname . ' (id INTEGER NOT NULL)'); if (DB::isError($res)) { return $res; } $res = $this->query("CREATE SEQUENCE ON ${seqname}"); return $res; } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_msql::nextID(), DB_msql::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ quoteIdentifier() /** * mSQL does not support delimited identifiers * * @param string $str the identifier name to be quoted * * @return object a DB_Error object * * @see DB_common::quoteIdentifier() * @since Method available since Release 1.7.0 */ function quoteIdentifier($str) { return $this->raiseError(DB_ERROR_UNSUPPORTED); } // }}} // {{{ quoteFloat() /** * Formats a float value for use within a query in a locale-independent * manner. * * @param float the float value to be quoted. * @return string the quoted string. * @see DB_common::quoteSmart() * @since Method available since release 1.7.8. */ function quoteFloat($float) { return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); } // }}} // {{{ escapeSimple() /** * Escapes a string according to the current DBMS's standards * * @param string $str the string to be escaped * * @return string the escaped string * * @see DB_common::quoteSmart() * @since Method available since Release 1.7.0 */ function escapeSimple($str) { return addslashes($str); } // }}} // {{{ msqlRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_msql::errorNative(), DB_msql::errorCode() */ function msqlRaiseError($errno = null) { $native = $this->errorNative(); if ($errno === null) { $errno = $this->errorCode($native); } return $this->raiseError($errno, null, null, null, $native); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error message produced by the last query * * @return string the DBMS' error message */ function errorNative() { return @msql_error(); } // }}} // {{{ errorCode() /** * Determines PEAR::DB error code from the database's text error message * * @param string $errormsg the error message returned from the database * * @return integer the error number from a DB_ERROR* constant */ function errorCode($errormsg) { static $error_regexps; // PHP 5.2+ prepends the function name to $php_errormsg, so we need // this hack to work around it, per bug #9599. $errormsg = preg_replace('/^msql[a-z_]+\(\): /', '', $errormsg); if (!isset($error_regexps)) { $error_regexps = array( '/^Access to database denied/i' => DB_ERROR_ACCESS_VIOLATION, '/^Bad index name/i' => DB_ERROR_ALREADY_EXISTS, '/^Bad order field/i' => DB_ERROR_SYNTAX, '/^Bad type for comparison/i' => DB_ERROR_SYNTAX, '/^Can\'t perform LIKE on/i' => DB_ERROR_SYNTAX, '/^Can\'t use TEXT fields in LIKE comparison/i' => DB_ERROR_SYNTAX, '/^Couldn\'t create temporary table/i' => DB_ERROR_CANNOT_CREATE, '/^Error creating table file/i' => DB_ERROR_CANNOT_CREATE, '/^Field .* cannot be null$/i' => DB_ERROR_CONSTRAINT_NOT_NULL, '/^Index (field|condition) .* cannot be null$/i' => DB_ERROR_SYNTAX, '/^Invalid date format/i' => DB_ERROR_INVALID_DATE, '/^Invalid time format/i' => DB_ERROR_INVALID, '/^Literal value for .* is wrong type$/i' => DB_ERROR_INVALID_NUMBER, '/^No Database Selected/i' => DB_ERROR_NODBSELECTED, '/^No value specified for field/i' => DB_ERROR_VALUE_COUNT_ON_ROW, '/^Non unique value for unique index/i' => DB_ERROR_CONSTRAINT, '/^Out of memory for temporary table/i' => DB_ERROR_CANNOT_CREATE, '/^Permission denied/i' => DB_ERROR_ACCESS_VIOLATION, '/^Reference to un-selected table/i' => DB_ERROR_SYNTAX, '/^syntax error/i' => DB_ERROR_SYNTAX, '/^Table .* exists$/i' => DB_ERROR_ALREADY_EXISTS, '/^Unknown database/i' => DB_ERROR_NOSUCHDB, '/^Unknown field/i' => DB_ERROR_NOSUCHFIELD, '/^Unknown (index|system variable)/i' => DB_ERROR_NOT_FOUND, '/^Unknown table/i' => DB_ERROR_NOSUCHTABLE, '/^Unqualified field/i' => DB_ERROR_SYNTAX, ); } foreach ($error_regexps as $regexp => $code) { if (preg_match($regexp, $errormsg)) { return $code; } } return DB_ERROR; } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::setOption() */ function tableInfo($result, $mode = null) { if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ $id = @msql_query("SELECT * FROM $result", $this->connection); $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ $id = $result; $got_string = false; } if (!is_resource($id)) { return $this->raiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = @msql_num_fields($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $tmp = @msql_fetch_field($id); $flags = ''; if ($tmp->not_null) { $flags .= 'not_null '; } if ($tmp->unique) { $flags .= 'unique_key '; } $flags = trim($flags); $res[$i] = array( 'table' => $case_func($tmp->table), 'name' => $case_func($tmp->name), 'type' => $tmp->type, 'len' => msql_field_len($id, $i), 'flags' => $flags, ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } // free the result only if we were called on a table if ($got_string) { @msql_free_result($id); } return $res; } // }}} // {{{ getSpecialQuery() /** * Obtain a list of a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return array the array containing the list of objects requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'databases': $id = @msql_list_dbs($this->connection); break; case 'tables': $id = @msql_list_tables($this->dsn['database'], $this->connection); break; default: return null; } if (!$id) { return $this->msqlRaiseError(); } $out = array(); while ($row = @msql_fetch_row($id)) { $out[] = $row[0]; } return $out; } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/mssql.php0000660000175100017510000007216411626162707014030 0ustar danielcdanielc * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: mssql.php 306603 2010-12-24 06:05:07Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's mssql extension * for interacting with Microsoft SQL Server databases * * These methods overload the ones declared in DB_common. * * DB's mssql driver is only for Microsfoft SQL Server databases. * * If you're connecting to a Sybase database, you MUST specify "sybase" * as the "phptype" in the DSN. * * This class only works correctly if you have compiled PHP using * --with-mssql=[dir_to_FreeTDS]. * * @category Database * @package DB * @author Sterling Hughes * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_mssql extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'mssql'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'mssql'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'emulate', 'new_link' => false, 'numrows' => true, 'pconnect' => true, 'prepare' => false, 'ssl' => false, 'transactions' => true, ); /** * A mapping of native error codes to DB error codes * @var array */ // XXX Add here error codes ie: 'S100E' => DB_ERROR_SYNTAX var $errorcode_map = array( 102 => DB_ERROR_SYNTAX, 110 => DB_ERROR_VALUE_COUNT_ON_ROW, 155 => DB_ERROR_NOSUCHFIELD, 156 => DB_ERROR_SYNTAX, 170 => DB_ERROR_SYNTAX, 207 => DB_ERROR_NOSUCHFIELD, 208 => DB_ERROR_NOSUCHTABLE, 245 => DB_ERROR_INVALID_NUMBER, 319 => DB_ERROR_SYNTAX, 321 => DB_ERROR_NOSUCHFIELD, 325 => DB_ERROR_SYNTAX, 336 => DB_ERROR_SYNTAX, 515 => DB_ERROR_CONSTRAINT_NOT_NULL, 547 => DB_ERROR_CONSTRAINT, 1018 => DB_ERROR_SYNTAX, 1035 => DB_ERROR_SYNTAX, 1913 => DB_ERROR_ALREADY_EXISTS, 2209 => DB_ERROR_SYNTAX, 2223 => DB_ERROR_SYNTAX, 2248 => DB_ERROR_SYNTAX, 2256 => DB_ERROR_SYNTAX, 2257 => DB_ERROR_SYNTAX, 2627 => DB_ERROR_CONSTRAINT, 2714 => DB_ERROR_ALREADY_EXISTS, 3607 => DB_ERROR_DIVZERO, 3701 => DB_ERROR_NOSUCHTABLE, 7630 => DB_ERROR_SYNTAX, 8134 => DB_ERROR_DIVZERO, 9303 => DB_ERROR_SYNTAX, 9317 => DB_ERROR_SYNTAX, 9318 => DB_ERROR_SYNTAX, 9331 => DB_ERROR_SYNTAX, 9332 => DB_ERROR_SYNTAX, 15253 => DB_ERROR_SYNTAX, ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * Should data manipulation queries be committed automatically? * @var bool * @access private */ var $autocommit = true; /** * The quantity of transactions begun * * {@internal While this is private, it can't actually be designated * private in PHP 5 because it is directly accessed in the test suite.}} * * @var integer * @access private */ var $transaction_opcount = 0; /** * The database specified in the DSN * * It's a fix to allow calls to different databases in the same script. * * @var string * @access private */ var $_db = null; // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_mssql() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('mssql') && !PEAR::loadExtension('sybase') && !PEAR::loadExtension('sybase_ct')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } $params = array( $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost', $dsn['username'] ? $dsn['username'] : null, $dsn['password'] ? $dsn['password'] : null, ); if ($dsn['port']) { $params[0] .= ((substr(PHP_OS, 0, 3) == 'WIN') ? ',' : ':') . $dsn['port']; } $connect_function = $persistent ? 'mssql_pconnect' : 'mssql_connect'; $this->connection = @call_user_func_array($connect_function, $params); if (!$this->connection) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, @mssql_get_last_message()); } if ($dsn['database']) { if (!@mssql_select_db($dsn['database'], $this->connection)) { return $this->raiseError(DB_ERROR_NODBSELECTED, null, null, null, @mssql_get_last_message()); } $this->_db = $dsn['database']; } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @mssql_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $ismanip = $this->_checkManip($query); $this->last_query = $query; if (!@mssql_select_db($this->_db, $this->connection)) { return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); } $query = $this->modifyQuery($query); if (!$this->autocommit && $ismanip) { if ($this->transaction_opcount == 0) { $result = @mssql_query('BEGIN TRAN', $this->connection); if (!$result) { return $this->mssqlRaiseError(); } } $this->transaction_opcount++; } $result = @mssql_query($query, $this->connection); if (!$result) { return $this->mssqlRaiseError(); } // Determine which queries that should return data, and which // should return an error code only. return $ismanip ? DB_OK : $result; } // }}} // {{{ nextResult() /** * Move the internal mssql result pointer to the next available result * * @param a valid fbsql result resource * * @access public * * @return true if a result is available otherwise return false */ function nextResult($result) { return @mssql_next_result($result); } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@mssql_data_seek($result, $rownum)) { return null; } } if ($fetchmode & DB_FETCHMODE_ASSOC) { $arr = @mssql_fetch_assoc($result); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $arr = @mssql_fetch_row($result); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? mssql_free_result($result) : false; } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @mssql_num_fields($result); if (!$cols) { return $this->mssqlRaiseError(); } return $cols; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($result) { $rows = @mssql_num_rows($result); if ($rows === false) { return $this->mssqlRaiseError(); } return $rows; } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { // XXX if $this->transaction_opcount > 0, we should probably // issue a warning here. $this->autocommit = $onoff ? true : false; return DB_OK; } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { if ($this->transaction_opcount > 0) { if (!@mssql_select_db($this->_db, $this->connection)) { return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); } $result = @mssql_query('COMMIT TRAN', $this->connection); $this->transaction_opcount = 0; if (!$result) { return $this->mssqlRaiseError(); } } return DB_OK; } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { if ($this->transaction_opcount > 0) { if (!@mssql_select_db($this->_db, $this->connection)) { return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); } $result = @mssql_query('ROLLBACK TRAN', $this->connection); $this->transaction_opcount = 0; if (!$result) { return $this->mssqlRaiseError(); } } return DB_OK; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if ($this->_last_query_manip) { $res = @mssql_query('select @@rowcount', $this->connection); if (!$res) { return $this->mssqlRaiseError(); } $ar = @mssql_fetch_row($res); if (!$ar) { $result = 0; } else { @mssql_free_result($res); $result = $ar[0]; } } else { $result = 0; } return $result; } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_mssql::createSequence(), DB_mssql::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); if (!@mssql_select_db($this->_db, $this->connection)) { return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); } $repeat = 0; do { $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); $this->popErrorHandling(); if ($ondemand && DB::isError($result) && ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) { $repeat = 1; $result = $this->createSequence($seq_name); if (DB::isError($result)) { return $this->raiseError($result); } } elseif (!DB::isError($result)) { $result = $this->query("SELECT IDENT_CURRENT('$seqname')"); if (DB::isError($result)) { /* Fallback code for MS SQL Server 7.0, which doesn't have * IDENT_CURRENT. This is *not* safe for concurrent * requests, and really, if you're using it, you're in a * world of hurt. Nevertheless, it's here to ensure BC. See * bug #181 for the gory details.*/ $result = $this->query("SELECT @@IDENTITY FROM $seqname"); } $repeat = 0; } else { $repeat = false; } } while ($repeat); if (DB::isError($result)) { return $this->raiseError($result); } $result = $result->fetchRow(DB_FETCHMODE_ORDERED); return $result[0]; } /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_mssql::nextID(), DB_mssql::dropSequence() */ function createSequence($seq_name) { return $this->query('CREATE TABLE ' . $this->getSequenceName($seq_name) . ' ([id] [int] IDENTITY (1, 1) NOT NULL,' . ' [vapor] [int] NULL)'); } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_mssql::nextID(), DB_mssql::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ escapeSimple() /** * Escapes a string in a manner suitable for SQL Server. * * @param string $str the string to be escaped * @return string the escaped string * * @see DB_common::quoteSmart() * @since Method available since Release 1.6.0 */ function escapeSimple($str) { return str_replace( array("'", "\\\r\n", "\\\n"), array("''", "\\\\\r\n\r\n", "\\\\\n\n"), $str ); } // }}} // {{{ quoteIdentifier() /** * Quotes a string so it can be safely used as a table or column name * * @param string $str identifier name to be quoted * * @return string quoted identifier string * * @see DB_common::quoteIdentifier() * @since Method available since Release 1.6.0 */ function quoteIdentifier($str) { return '[' . str_replace(']', ']]', $str) . ']'; } // }}} // {{{ mssqlRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_mssql::errorNative(), DB_mssql::errorCode() */ function mssqlRaiseError($code = null) { $message = @mssql_get_last_message(); if (!$code) { $code = $this->errorNative(); } return $this->raiseError($this->errorCode($code, $message), null, null, null, "$code - $message"); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error code produced by the last query * * @return int the DBMS' error code */ function errorNative() { $res = @mssql_query('select @@ERROR as ErrorCode', $this->connection); if (!$res) { return DB_ERROR; } $row = @mssql_fetch_row($res); return $row[0]; } // }}} // {{{ errorCode() /** * Determines PEAR::DB error code from mssql's native codes. * * If $nativecode isn't known yet, it will be looked up. * * @param mixed $nativecode mssql error code, if known * @return integer an error number from a DB error constant * @see errorNative() */ function errorCode($nativecode = null, $msg = '') { if (!$nativecode) { $nativecode = $this->errorNative(); } if (isset($this->errorcode_map[$nativecode])) { if ($nativecode == 3701 && preg_match('/Cannot drop the index/i', $msg)) { return DB_ERROR_NOT_FOUND; } return $this->errorcode_map[$nativecode]; } else { return DB_ERROR; } } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) { if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ if (!@mssql_select_db($this->_db, $this->connection)) { return $this->mssqlRaiseError(DB_ERROR_NODBSELECTED); } $id = @mssql_query("SELECT * FROM $result WHERE 1=0", $this->connection); $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ $id = $result; $got_string = false; } if (!is_resource($id)) { return $this->mssqlRaiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = @mssql_num_fields($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { if ($got_string) { $flags = $this->_mssql_field_flags($result, @mssql_field_name($id, $i)); if (DB::isError($flags)) { return $flags; } } else { $flags = ''; } $res[$i] = array( 'table' => $got_string ? $case_func($result) : '', 'name' => $case_func(@mssql_field_name($id, $i)), 'type' => @mssql_field_type($id, $i), 'len' => @mssql_field_length($id, $i), 'flags' => $flags, ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } // free the result only if we were called on a table if ($got_string) { @mssql_free_result($id); } return $res; } // }}} // {{{ _mssql_field_flags() /** * Get a column's flags * * Supports "not_null", "primary_key", * "auto_increment" (mssql identity), "timestamp" (mssql timestamp), * "unique_key" (mssql unique index, unique check or primary_key) and * "multiple_key" (multikey index) * * mssql timestamp is NOT similar to the mysql timestamp so this is maybe * not useful at all - is the behaviour of mysql_field_flags that primary * keys are alway unique? is the interpretation of multiple_key correct? * * @param string $table the table name * @param string $column the field name * * @return string the flags * * @access private * @author Joern Barthel */ function _mssql_field_flags($table, $column) { static $tableName = null; static $flags = array(); if ($table != $tableName) { $flags = array(); $tableName = $table; // get unique and primary keys $res = $this->getAll("EXEC SP_HELPINDEX $table", DB_FETCHMODE_ASSOC); if (DB::isError($res)) { return $res; } foreach ($res as $val) { $keys = explode(', ', $val['index_keys']); if (sizeof($keys) > 1) { foreach ($keys as $key) { $this->_add_flag($flags[$key], 'multiple_key'); } } if (strpos($val['index_description'], 'primary key')) { foreach ($keys as $key) { $this->_add_flag($flags[$key], 'primary_key'); } } elseif (strpos($val['index_description'], 'unique')) { foreach ($keys as $key) { $this->_add_flag($flags[$key], 'unique_key'); } } } // get auto_increment, not_null and timestamp $res = $this->getAll("EXEC SP_COLUMNS $table", DB_FETCHMODE_ASSOC); if (DB::isError($res)) { return $res; } foreach ($res as $val) { $val = array_change_key_case($val, CASE_LOWER); if ($val['nullable'] == '0') { $this->_add_flag($flags[$val['column_name']], 'not_null'); } if (strpos($val['type_name'], 'identity')) { $this->_add_flag($flags[$val['column_name']], 'auto_increment'); } if (strpos($val['type_name'], 'timestamp')) { $this->_add_flag($flags[$val['column_name']], 'timestamp'); } } } if (array_key_exists($column, $flags)) { return(implode(' ', $flags[$column])); } return ''; } // }}} // {{{ _add_flag() /** * Adds a string to the flags array if the flag is not yet in there * - if there is no flag present the array is created * * @param array &$array the reference to the flag-array * @param string $value the flag value * * @return void * * @access private * @author Joern Barthel */ function _add_flag(&$array, $value) { if (!is_array($array)) { $array = array($value); } elseif (!in_array($value, $array)) { array_push($array, $value); } } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return "SELECT name FROM sysobjects WHERE type = 'U'" . ' ORDER BY name'; case 'views': return "SELECT name FROM sysobjects WHERE type = 'V'"; default: return null; } } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/mysql.php0000660000175100017510000010007211626162707014024 0ustar danielcdanielc * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: mysql.php 242769 2007-09-21 13:32:52Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's mysql extension * for interacting with MySQL databases * * These methods overload the ones declared in DB_common. * * @category Database * @package DB * @author Stig Bakken * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_mysql extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'mysql'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'mysql'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'alter', 'new_link' => '4.2.0', 'numrows' => true, 'pconnect' => true, 'prepare' => false, 'ssl' => false, 'transactions' => true, ); /** * A mapping of native error codes to DB error codes * @var array */ var $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, 1022 => DB_ERROR_ALREADY_EXISTS, 1044 => DB_ERROR_ACCESS_VIOLATION, 1046 => DB_ERROR_NODBSELECTED, 1048 => DB_ERROR_CONSTRAINT, 1049 => DB_ERROR_NOSUCHDB, 1050 => DB_ERROR_ALREADY_EXISTS, 1051 => DB_ERROR_NOSUCHTABLE, 1054 => DB_ERROR_NOSUCHFIELD, 1061 => DB_ERROR_ALREADY_EXISTS, 1062 => DB_ERROR_ALREADY_EXISTS, 1064 => DB_ERROR_SYNTAX, 1091 => DB_ERROR_NOT_FOUND, 1100 => DB_ERROR_NOT_LOCKED, 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, 1142 => DB_ERROR_ACCESS_VIOLATION, 1146 => DB_ERROR_NOSUCHTABLE, 1216 => DB_ERROR_CONSTRAINT, 1217 => DB_ERROR_CONSTRAINT, 1356 => DB_ERROR_DIVZERO, 1451 => DB_ERROR_CONSTRAINT, 1452 => DB_ERROR_CONSTRAINT, ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * Should data manipulation queries be committed automatically? * @var bool * @access private */ var $autocommit = true; /** * The quantity of transactions begun * * {@internal While this is private, it can't actually be designated * private in PHP 5 because it is directly accessed in the test suite.}} * * @var integer * @access private */ var $transaction_opcount = 0; /** * The database specified in the DSN * * It's a fix to allow calls to different databases in the same script. * * @var string * @access private */ var $_db = ''; // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_mysql() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * PEAR DB's mysql driver supports the following extra DSN options: * + new_link If set to true, causes subsequent calls to connect() * to return a new connection link instead of the * existing one. WARNING: this is not portable to * other DBMS's. Available since PEAR DB 1.7.0. * + client_flags Any combination of MYSQL_CLIENT_* constants. * Only used if PHP is at version 4.3.0 or greater. * Available since PEAR DB 1.7.0. * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('mysql')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } $params = array(); if ($dsn['protocol'] && $dsn['protocol'] == 'unix') { $params[0] = ':' . $dsn['socket']; } else { $params[0] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost'; if ($dsn['port']) { $params[0] .= ':' . $dsn['port']; } } $params[] = $dsn['username'] ? $dsn['username'] : null; $params[] = $dsn['password'] ? $dsn['password'] : null; if (!$persistent) { if (isset($dsn['new_link']) && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) { $params[] = true; } else { $params[] = false; } } if (version_compare(phpversion(), '4.3.0', '>=')) { $params[] = isset($dsn['client_flags']) ? $dsn['client_flags'] : null; } $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect'; $ini = ini_get('track_errors'); $php_errormsg = ''; if ($ini) { $this->connection = @call_user_func_array($connect_function, $params); } else { @ini_set('track_errors', 1); $this->connection = @call_user_func_array($connect_function, $params); @ini_set('track_errors', $ini); } if (!$this->connection) { if (($err = @mysql_error()) != '') { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $err); } else { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $php_errormsg); } } if ($dsn['database']) { if (!@mysql_select_db($dsn['database'], $this->connection)) { return $this->mysqlRaiseError(); } $this->_db = $dsn['database']; } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @mysql_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * Generally uses mysql_query(). If you want to use * mysql_unbuffered_query() set the "result_buffering" option to 0 using * setOptions(). This option was added in Release 1.7.0. * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $ismanip = $this->_checkManip($query); $this->last_query = $query; $query = $this->modifyQuery($query); if ($this->_db) { if (!@mysql_select_db($this->_db, $this->connection)) { return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); } } if (!$this->autocommit && $ismanip) { if ($this->transaction_opcount == 0) { $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection); $result = @mysql_query('BEGIN', $this->connection); if (!$result) { return $this->mysqlRaiseError(); } } $this->transaction_opcount++; } if (!$this->options['result_buffering']) { $result = @mysql_unbuffered_query($query, $this->connection); } else { $result = @mysql_query($query, $this->connection); } if (!$result) { return $this->mysqlRaiseError(); } if (is_resource($result)) { return $result; } return DB_OK; } // }}} // {{{ nextResult() /** * Move the internal mysql result pointer to the next available result * * This method has not been implemented yet. * * @param a valid sql result resource * * @return false */ function nextResult($result) { return false; } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@mysql_data_seek($result, $rownum)) { return null; } } if ($fetchmode & DB_FETCHMODE_ASSOC) { $arr = @mysql_fetch_array($result, MYSQL_ASSOC); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $arr = @mysql_fetch_row($result); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { /* * Even though this DBMS already trims output, we do this because * a field might have intentional whitespace at the end that * gets removed by DB_PORTABILITY_RTRIM under another driver. */ $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? mysql_free_result($result) : false; } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @mysql_num_fields($result); if (!$cols) { return $this->mysqlRaiseError(); } return $cols; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($result) { $rows = @mysql_num_rows($result); if ($rows === null) { return $this->mysqlRaiseError(); } return $rows; } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { // XXX if $this->transaction_opcount > 0, we should probably // issue a warning here. $this->autocommit = $onoff ? true : false; return DB_OK; } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { if ($this->transaction_opcount > 0) { if ($this->_db) { if (!@mysql_select_db($this->_db, $this->connection)) { return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); } } $result = @mysql_query('COMMIT', $this->connection); $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); $this->transaction_opcount = 0; if (!$result) { return $this->mysqlRaiseError(); } } return DB_OK; } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { if ($this->transaction_opcount > 0) { if ($this->_db) { if (!@mysql_select_db($this->_db, $this->connection)) { return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); } } $result = @mysql_query('ROLLBACK', $this->connection); $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection); $this->transaction_opcount = 0; if (!$result) { return $this->mysqlRaiseError(); } } return DB_OK; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if ($this->_last_query_manip) { return @mysql_affected_rows($this->connection); } else { return 0; } } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_mysql::createSequence(), DB_mysql::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); do { $repeat = 0; $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query("UPDATE ${seqname} ". 'SET id=LAST_INSERT_ID(id+1)'); $this->popErrorHandling(); if ($result === DB_OK) { // COMMON CASE $id = @mysql_insert_id($this->connection); if ($id != 0) { return $id; } // EMPTY SEQ TABLE // Sequence table must be empty for some reason, so fill // it and return 1 and obtain a user-level lock $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); if (DB::isError($result)) { return $this->raiseError($result); } if ($result == 0) { // Failed to get the lock return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); } // add the default value $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)"); if (DB::isError($result)) { return $this->raiseError($result); } // Release the lock $result = $this->getOne('SELECT RELEASE_LOCK(' . "'${seqname}_lock')"); if (DB::isError($result)) { return $this->raiseError($result); } // We know what the result will be, so no need to try again return 1; } elseif ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { // ONDEMAND TABLE CREATION $result = $this->createSequence($seq_name); if (DB::isError($result)) { return $this->raiseError($result); } else { $repeat = 1; } } elseif (DB::isError($result) && $result->getCode() == DB_ERROR_ALREADY_EXISTS) { // BACKWARDS COMPAT // see _BCsequence() comment $result = $this->_BCsequence($seqname); if (DB::isError($result)) { return $this->raiseError($result); } $repeat = 1; } } while ($repeat); return $this->raiseError($result); } // }}} // {{{ createSequence() /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_mysql::nextID(), DB_mysql::dropSequence() */ function createSequence($seq_name) { $seqname = $this->getSequenceName($seq_name); $res = $this->query('CREATE TABLE ' . $seqname . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,' . ' PRIMARY KEY(id))'); if (DB::isError($res)) { return $res; } // insert yields value 1, nextId call will generate ID 2 $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)"); if (DB::isError($res)) { return $res; } // so reset to zero return $this->query("UPDATE ${seqname} SET id = 0"); } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_mysql::nextID(), DB_mysql::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ _BCsequence() /** * Backwards compatibility with old sequence emulation implementation * (clean up the dupes) * * @param string $seqname the sequence name to clean up * * @return bool true on success. A DB_Error object on failure. * * @access private */ function _BCsequence($seqname) { // Obtain a user-level lock... this will release any previous // application locks, but unlike LOCK TABLES, it does not abort // the current transaction and is much less frequently used. $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); if (DB::isError($result)) { return $result; } if ($result == 0) { // Failed to get the lock, can't do the conversion, bail // with a DB_ERROR_NOT_LOCKED error return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED); } $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); if (DB::isError($highest_id)) { return $highest_id; } // This should kill all rows except the highest // We should probably do something if $highest_id isn't // numeric, but I'm at a loss as how to handle that... $result = $this->query('DELETE FROM ' . $seqname . " WHERE id <> $highest_id"); if (DB::isError($result)) { return $result; } // If another thread has been waiting for this lock, // it will go thru the above procedure, but will have no // real effect $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); if (DB::isError($result)) { return $result; } return true; } // }}} // {{{ quoteIdentifier() /** * Quotes a string so it can be safely used as a table or column name * (WARNING: using names that require this is a REALLY BAD IDEA) * * WARNING: Older versions of MySQL can't handle the backtick * character (`) in table or column names. * * @param string $str identifier name to be quoted * * @return string quoted identifier string * * @see DB_common::quoteIdentifier() * @since Method available since Release 1.6.0 */ function quoteIdentifier($str) { return '`' . str_replace('`', '``', $str) . '`'; } // }}} // {{{ quote() /** * @deprecated Deprecated in release 1.6.0 */ function quote($str) { return $this->quoteSmart($str); } // }}} // {{{ escapeSimple() /** * Escapes a string according to the current DBMS's standards * * @param string $str the string to be escaped * * @return string the escaped string * * @see DB_common::quoteSmart() * @since Method available since Release 1.6.0 */ function escapeSimple($str) { if (function_exists('mysql_real_escape_string')) { return @mysql_real_escape_string($str, $this->connection); } else { return @mysql_escape_string($str); } } // }}} // {{{ modifyQuery() /** * Changes a query string for various DBMS specific reasons * * This little hack lets you know how many rows were deleted * when running a "DELETE FROM table" query. Only implemented * if the DB_PORTABILITY_DELETE_COUNT portability option is on. * * @param string $query the query string to modify * * @return string the modified query string * * @access protected * @see DB_common::setOption() */ function modifyQuery($query) { if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { // "DELETE FROM table" gives 0 affected rows in MySQL. // This little hack lets you know how many rows were deleted. if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', 'DELETE FROM \1 WHERE 1=1', $query); } } return $query; } // }}} // {{{ modifyLimitQuery() /** * Adds LIMIT clauses to a query string according to current DBMS standards * * @param string $query the query to modify * @param int $from the row to start to fetching (0 = the first row) * @param int $count the numbers of rows to fetch * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return string the query string with LIMIT clauses added * * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { if (DB::isManip($query) || $this->_next_query_manip) { return $query . " LIMIT $count"; } else { return $query . " LIMIT $from, $count"; } } // }}} // {{{ mysqlRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_mysql::errorNative(), DB_common::errorCode() */ function mysqlRaiseError($errno = null) { if ($errno === null) { if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT; $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL; $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT; } else { // Doing this in case mode changes during runtime. $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS; $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT; $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS; } $errno = $this->errorCode(mysql_errno($this->connection)); } return $this->raiseError($errno, null, null, null, @mysql_errno($this->connection) . ' ** ' . @mysql_error($this->connection)); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error code produced by the last query * * @return int the DBMS' error code */ function errorNative() { return @mysql_errno($this->connection); } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) { if (is_string($result)) { // Fix for bug #11580. if ($this->_db) { if (!@mysql_select_db($this->_db, $this->connection)) { return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED); } } /* * Probably received a table name. * Create a result resource identifier. */ $id = @mysql_query("SELECT * FROM $result LIMIT 0", $this->connection); $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ $id = $result; $got_string = false; } if (!is_resource($id)) { return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = @mysql_num_fields($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $res[$i] = array( 'table' => $case_func(@mysql_field_table($id, $i)), 'name' => $case_func(@mysql_field_name($id, $i)), 'type' => @mysql_field_type($id, $i), 'len' => @mysql_field_len($id, $i), 'flags' => @mysql_field_flags($id, $i), ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } // free the result only if we were called on a table if ($got_string) { @mysql_free_result($id); } return $res; } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return 'SHOW TABLES'; case 'users': return 'SELECT DISTINCT User FROM mysql.user'; case 'databases': return 'SHOW DATABASES'; default: return null; } } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/mysqli.php0000660000175100017510000010361411626162707014202 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: mysqli.php 315557 2011-08-26 14:32:35Z danielc $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's mysqli extension * for interacting with MySQL databases * * This is for MySQL versions 4.1 and above. Requires PHP 5. * * Note that persistent connections no longer exist. * * These methods overload the ones declared in DB_common. * * @category Database * @package DB * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB * @since Class functional since Release 1.6.3 */ class DB_mysqli extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'mysqli'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'mysqli'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'alter', 'new_link' => false, 'numrows' => true, 'pconnect' => false, 'prepare' => false, 'ssl' => true, 'transactions' => true, ); /** * A mapping of native error codes to DB error codes * @var array */ var $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, 1022 => DB_ERROR_ALREADY_EXISTS, 1044 => DB_ERROR_ACCESS_VIOLATION, 1046 => DB_ERROR_NODBSELECTED, 1048 => DB_ERROR_CONSTRAINT, 1049 => DB_ERROR_NOSUCHDB, 1050 => DB_ERROR_ALREADY_EXISTS, 1051 => DB_ERROR_NOSUCHTABLE, 1054 => DB_ERROR_NOSUCHFIELD, 1061 => DB_ERROR_ALREADY_EXISTS, 1062 => DB_ERROR_ALREADY_EXISTS, 1064 => DB_ERROR_SYNTAX, 1091 => DB_ERROR_NOT_FOUND, 1100 => DB_ERROR_NOT_LOCKED, 1136 => DB_ERROR_VALUE_COUNT_ON_ROW, 1142 => DB_ERROR_ACCESS_VIOLATION, 1146 => DB_ERROR_NOSUCHTABLE, 1216 => DB_ERROR_CONSTRAINT, 1217 => DB_ERROR_CONSTRAINT, 1356 => DB_ERROR_DIVZERO, 1451 => DB_ERROR_CONSTRAINT, 1452 => DB_ERROR_CONSTRAINT, ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * Should data manipulation queries be committed automatically? * @var bool * @access private */ var $autocommit = true; /** * The quantity of transactions begun * * {@internal While this is private, it can't actually be designated * private in PHP 5 because it is directly accessed in the test suite.}} * * @var integer * @access private */ var $transaction_opcount = 0; /** * The database specified in the DSN * * It's a fix to allow calls to different databases in the same script. * * @var string * @access private */ var $_db = ''; /** * Array for converting MYSQLI_*_FLAG constants to text values * @var array * @access public * @since Property available since Release 1.6.5 */ var $mysqli_flags = array( MYSQLI_NOT_NULL_FLAG => 'not_null', MYSQLI_PRI_KEY_FLAG => 'primary_key', MYSQLI_UNIQUE_KEY_FLAG => 'unique_key', MYSQLI_MULTIPLE_KEY_FLAG => 'multiple_key', MYSQLI_BLOB_FLAG => 'blob', MYSQLI_UNSIGNED_FLAG => 'unsigned', MYSQLI_ZEROFILL_FLAG => 'zerofill', MYSQLI_AUTO_INCREMENT_FLAG => 'auto_increment', MYSQLI_TIMESTAMP_FLAG => 'timestamp', MYSQLI_SET_FLAG => 'set', // MYSQLI_NUM_FLAG => 'numeric', // unnecessary // MYSQLI_PART_KEY_FLAG => 'multiple_key', // duplicatvie MYSQLI_GROUP_FLAG => 'group_by' ); /** * Array for converting MYSQLI_TYPE_* constants to text values * @var array * @access public * @since Property available since Release 1.6.5 */ var $mysqli_types = array( MYSQLI_TYPE_DECIMAL => 'decimal', MYSQLI_TYPE_TINY => 'tinyint', MYSQLI_TYPE_SHORT => 'int', MYSQLI_TYPE_LONG => 'int', MYSQLI_TYPE_FLOAT => 'float', MYSQLI_TYPE_DOUBLE => 'double', // MYSQLI_TYPE_NULL => 'DEFAULT NULL', // let flags handle it MYSQLI_TYPE_TIMESTAMP => 'timestamp', MYSQLI_TYPE_LONGLONG => 'bigint', MYSQLI_TYPE_INT24 => 'mediumint', MYSQLI_TYPE_DATE => 'date', MYSQLI_TYPE_TIME => 'time', MYSQLI_TYPE_DATETIME => 'datetime', MYSQLI_TYPE_YEAR => 'year', MYSQLI_TYPE_NEWDATE => 'date', MYSQLI_TYPE_ENUM => 'enum', MYSQLI_TYPE_SET => 'set', MYSQLI_TYPE_TINY_BLOB => 'tinyblob', MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob', MYSQLI_TYPE_LONG_BLOB => 'longblob', MYSQLI_TYPE_BLOB => 'blob', MYSQLI_TYPE_VAR_STRING => 'varchar', MYSQLI_TYPE_STRING => 'char', MYSQLI_TYPE_GEOMETRY => 'geometry', /* These constants are conditionally compiled in ext/mysqli, so we'll * define them by number rather than constant. */ 16 => 'bit', 246 => 'decimal', ); // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_mysqli() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * PEAR DB's mysqli driver supports the following extra DSN options: * + When the 'ssl' $option passed to DB::connect() is true: * + key The path to the key file. * + cert The path to the certificate file. * + ca The path to the certificate authority file. * + capath The path to a directory that contains trusted SSL * CA certificates in pem format. * + cipher The list of allowable ciphers for SSL encryption. * * Example of how to connect using SSL: * * require_once 'DB.php'; * * $dsn = array( * 'phptype' => 'mysqli', * 'username' => 'someuser', * 'password' => 'apasswd', * 'hostspec' => 'localhost', * 'database' => 'thedb', * 'key' => 'client-key.pem', * 'cert' => 'client-cert.pem', * 'ca' => 'cacert.pem', * 'capath' => '/path/to/ca/dir', * 'cipher' => 'AES', * ); * * $options = array( * 'ssl' => true, * ); * * $db = DB::connect($dsn, $options); * if (PEAR::isError($db)) { * die($db->getMessage()); * } * * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('mysqli')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } $ini = ini_get('track_errors'); @ini_set('track_errors', 1); $php_errormsg = ''; if (((int) $this->getOption('ssl')) === 1) { $init = mysqli_init(); mysqli_ssl_set( $init, empty($dsn['key']) ? null : $dsn['key'], empty($dsn['cert']) ? null : $dsn['cert'], empty($dsn['ca']) ? null : $dsn['ca'], empty($dsn['capath']) ? null : $dsn['capath'], empty($dsn['cipher']) ? null : $dsn['cipher'] ); if ($this->connection = @mysqli_real_connect( $init, $dsn['hostspec'], $dsn['username'], $dsn['password'], $dsn['database'], $dsn['port'], $dsn['socket'])) { $this->connection = $init; } } else { $this->connection = @mysqli_connect( $dsn['hostspec'], $dsn['username'], $dsn['password'], $dsn['database'], $dsn['port'], $dsn['socket'] ); } @ini_set('track_errors', $ini); if (!$this->connection) { if (($err = @mysqli_connect_error()) != '') { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $err); } else { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $php_errormsg); } } if ($dsn['database']) { $this->_db = $dsn['database']; } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @mysqli_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $ismanip = $this->_checkManip($query); $this->last_query = $query; $query = $this->modifyQuery($query); if ($this->_db) { if (!@mysqli_select_db($this->connection, $this->_db)) { return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); } } if (!$this->autocommit && $ismanip) { if ($this->transaction_opcount == 0) { $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0'); $result = @mysqli_query($this->connection, 'BEGIN'); if (!$result) { return $this->mysqliRaiseError(); } } $this->transaction_opcount++; } $result = @mysqli_query($this->connection, $query); if (!$result) { return $this->mysqliRaiseError(); } if (is_object($result)) { return $result; } return DB_OK; } // }}} // {{{ nextResult() /** * Move the internal mysql result pointer to the next available result. * * This method has not been implemented yet. * * @param resource $result a valid sql result resource * @return false * @access public */ function nextResult($result) { return false; } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@mysqli_data_seek($result, $rownum)) { return null; } } if ($fetchmode & DB_FETCHMODE_ASSOC) { $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $arr = @mysqli_fetch_row($result); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { /* * Even though this DBMS already trims output, we do this because * a field might have intentional whitespace at the end that * gets removed by DB_PORTABILITY_RTRIM under another driver. */ $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? mysqli_free_result($result) : false; } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @mysqli_num_fields($result); if (!$cols) { return $this->mysqliRaiseError(); } return $cols; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($result) { $rows = @mysqli_num_rows($result); if ($rows === null) { return $this->mysqliRaiseError(); } return $rows; } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { // XXX if $this->transaction_opcount > 0, we should probably // issue a warning here. $this->autocommit = $onoff ? true : false; return DB_OK; } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { if ($this->transaction_opcount > 0) { if ($this->_db) { if (!@mysqli_select_db($this->connection, $this->_db)) { return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); } } $result = @mysqli_query($this->connection, 'COMMIT'); $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); $this->transaction_opcount = 0; if (!$result) { return $this->mysqliRaiseError(); } } return DB_OK; } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { if ($this->transaction_opcount > 0) { if ($this->_db) { if (!@mysqli_select_db($this->connection, $this->_db)) { return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); } } $result = @mysqli_query($this->connection, 'ROLLBACK'); $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1'); $this->transaction_opcount = 0; if (!$result) { return $this->mysqliRaiseError(); } } return DB_OK; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if ($this->_last_query_manip) { return @mysqli_affected_rows($this->connection); } else { return 0; } } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_mysqli::createSequence(), DB_mysqli::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); do { $repeat = 0; $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query('UPDATE ' . $seqname . ' SET id = LAST_INSERT_ID(id + 1)'); $this->popErrorHandling(); if ($result === DB_OK) { // COMMON CASE $id = @mysqli_insert_id($this->connection); if ($id != 0) { return $id; } // EMPTY SEQ TABLE // Sequence table must be empty for some reason, // so fill it and return 1 // Obtain a user-level lock $result = $this->getOne('SELECT GET_LOCK(' . "'${seqname}_lock', 10)"); if (DB::isError($result)) { return $this->raiseError($result); } if ($result == 0) { return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED); } // add the default value $result = $this->query('REPLACE INTO ' . $seqname . ' (id) VALUES (0)'); if (DB::isError($result)) { return $this->raiseError($result); } // Release the lock $result = $this->getOne('SELECT RELEASE_LOCK(' . "'${seqname}_lock')"); if (DB::isError($result)) { return $this->raiseError($result); } // We know what the result will be, so no need to try again return 1; } elseif ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { // ONDEMAND TABLE CREATION $result = $this->createSequence($seq_name); // Since createSequence initializes the ID to be 1, // we do not need to retrieve the ID again (or we will get 2) if (DB::isError($result)) { return $this->raiseError($result); } else { // First ID of a newly created sequence is 1 return 1; } } elseif (DB::isError($result) && $result->getCode() == DB_ERROR_ALREADY_EXISTS) { // BACKWARDS COMPAT // see _BCsequence() comment $result = $this->_BCsequence($seqname); if (DB::isError($result)) { return $this->raiseError($result); } $repeat = 1; } } while ($repeat); return $this->raiseError($result); } /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_mysqli::nextID(), DB_mysqli::dropSequence() */ function createSequence($seq_name) { $seqname = $this->getSequenceName($seq_name); $res = $this->query('CREATE TABLE ' . $seqname . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,' . ' PRIMARY KEY(id))'); if (DB::isError($res)) { return $res; } // insert yields value 1, nextId call will generate ID 2 return $this->query("INSERT INTO ${seqname} (id) VALUES (0)"); } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_mysql::nextID(), DB_mysql::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ _BCsequence() /** * Backwards compatibility with old sequence emulation implementation * (clean up the dupes) * * @param string $seqname the sequence name to clean up * * @return bool true on success. A DB_Error object on failure. * * @access private */ function _BCsequence($seqname) { // Obtain a user-level lock... this will release any previous // application locks, but unlike LOCK TABLES, it does not abort // the current transaction and is much less frequently used. $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)"); if (DB::isError($result)) { return $result; } if ($result == 0) { // Failed to get the lock, can't do the conversion, bail // with a DB_ERROR_NOT_LOCKED error return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED); } $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}"); if (DB::isError($highest_id)) { return $highest_id; } // This should kill all rows except the highest // We should probably do something if $highest_id isn't // numeric, but I'm at a loss as how to handle that... $result = $this->query('DELETE FROM ' . $seqname . " WHERE id <> $highest_id"); if (DB::isError($result)) { return $result; } // If another thread has been waiting for this lock, // it will go thru the above procedure, but will have no // real effect $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')"); if (DB::isError($result)) { return $result; } return true; } // }}} // {{{ quoteIdentifier() /** * Quotes a string so it can be safely used as a table or column name * (WARNING: using names that require this is a REALLY BAD IDEA) * * WARNING: Older versions of MySQL can't handle the backtick * character (`) in table or column names. * * @param string $str identifier name to be quoted * * @return string quoted identifier string * * @see DB_common::quoteIdentifier() * @since Method available since Release 1.6.0 */ function quoteIdentifier($str) { return '`' . str_replace('`', '``', $str) . '`'; } // }}} // {{{ escapeSimple() /** * Escapes a string according to the current DBMS's standards * * @param string $str the string to be escaped * * @return string the escaped string * * @see DB_common::quoteSmart() * @since Method available since Release 1.6.0 */ function escapeSimple($str) { return @mysqli_real_escape_string($this->connection, $str); } // }}} // {{{ modifyLimitQuery() /** * Adds LIMIT clauses to a query string according to current DBMS standards * * @param string $query the query to modify * @param int $from the row to start to fetching (0 = the first row) * @param int $count the numbers of rows to fetch * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return string the query string with LIMIT clauses added * * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { if (DB::isManip($query) || $this->_next_query_manip) { return $query . " LIMIT $count"; } else { return $query . " LIMIT $from, $count"; } } // }}} // {{{ mysqliRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_mysqli::errorNative(), DB_common::errorCode() */ function mysqliRaiseError($errno = null) { if ($errno === null) { if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT; $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL; $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT; } else { // Doing this in case mode changes during runtime. $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS; $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT; $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS; } $errno = $this->errorCode(mysqli_errno($this->connection)); } return $this->raiseError($errno, null, null, null, @mysqli_errno($this->connection) . ' ** ' . @mysqli_error($this->connection)); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error code produced by the last query * * @return int the DBMS' error code */ function errorNative() { return @mysqli_errno($this->connection); } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::setOption() */ function tableInfo($result, $mode = null) { if (is_string($result)) { // Fix for bug #11580. if ($this->_db) { if (!@mysqli_select_db($this->connection, $this->_db)) { return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED); } } /* * Probably received a table name. * Create a result resource identifier. */ $id = @mysqli_query($this->connection, "SELECT * FROM $result LIMIT 0"); $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ $id = $result; $got_string = false; } if (!is_object($id) || !is_a($id, 'mysqli_result')) { return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = @mysqli_num_fields($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $tmp = @mysqli_fetch_field($id); $flags = ''; foreach ($this->mysqli_flags as $const => $means) { if ($tmp->flags & $const) { $flags .= $means . ' '; } } if ($tmp->def) { $flags .= 'default_' . rawurlencode($tmp->def); } $flags = trim($flags); $res[$i] = array( 'table' => $case_func($tmp->table), 'name' => $case_func($tmp->name), 'type' => isset($this->mysqli_types[$tmp->type]) ? $this->mysqli_types[$tmp->type] : 'unknown', // http://bugs.php.net/?id=36579 'len' => $tmp->length, 'flags' => $flags, ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } // free the result only if we were called on a table if ($got_string) { @mysqli_free_result($id); } return $res; } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return 'SHOW TABLES'; case 'users': return 'SELECT DISTINCT User FROM mysql.user'; case 'databases': return 'SHOW DATABASES'; default: return null; } } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/oci8.php0000660000175100017510000011145311626162707013526 0ustar danielcdanielc * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: oci8.php 293241 2010-01-08 05:00:28Z danielc $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's oci8 extension * for interacting with Oracle databases * * Definitely works with versions 8 and 9 of Oracle. * * These methods overload the ones declared in DB_common. * * Be aware... OCIError() only appears to return anything when given a * statement, so functions return the generic DB_ERROR instead of more * useful errors that have to do with feedback from the database. * * @category Database * @package DB * @author James L. Pine * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_oci8 extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'oci8'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'oci8'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'alter', 'new_link' => '5.0.0', 'numrows' => 'subquery', 'pconnect' => true, 'prepare' => true, 'ssl' => false, 'transactions' => true, ); /** * A mapping of native error codes to DB error codes * @var array */ var $errorcode_map = array( 1 => DB_ERROR_CONSTRAINT, 900 => DB_ERROR_SYNTAX, 904 => DB_ERROR_NOSUCHFIELD, 913 => DB_ERROR_VALUE_COUNT_ON_ROW, 921 => DB_ERROR_SYNTAX, 923 => DB_ERROR_SYNTAX, 942 => DB_ERROR_NOSUCHTABLE, 955 => DB_ERROR_ALREADY_EXISTS, 1400 => DB_ERROR_CONSTRAINT_NOT_NULL, 1401 => DB_ERROR_INVALID, 1407 => DB_ERROR_CONSTRAINT_NOT_NULL, 1418 => DB_ERROR_NOT_FOUND, 1476 => DB_ERROR_DIVZERO, 1722 => DB_ERROR_INVALID_NUMBER, 2289 => DB_ERROR_NOSUCHTABLE, 2291 => DB_ERROR_CONSTRAINT, 2292 => DB_ERROR_CONSTRAINT, 2449 => DB_ERROR_CONSTRAINT, 12899 => DB_ERROR_INVALID, ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * Should data manipulation queries be committed automatically? * @var bool * @access private */ var $autocommit = true; /** * Stores the $data passed to execute() in the oci8 driver * * Gets reset to array() when simpleQuery() is run. * * Needed in case user wants to call numRows() after prepare/execute * was used. * * @var array * @access private */ var $_data = array(); /** * The result or statement handle from the most recently executed query * @var resource */ var $last_stmt; /** * Is the given prepared statement a data manipulation query? * @var array * @access private */ var $manip_query = array(); /** * Store of prepared SQL queries. * @var array * @access private */ var $_prepared_queries = array(); // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_oci8() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * If PHP is at version 5.0.0 or greater: * + Generally, oci_connect() or oci_pconnect() are used. * + But if the new_link DSN option is set to true, oci_new_connect() * is used. * * When using PHP version 4.x, OCILogon() or OCIPLogon() are used. * * PEAR DB's oci8 driver supports the following extra DSN options: * + charset The character set to be used on the connection. * Only used if PHP is at version 5.0.0 or greater * and the Oracle server is at 9.2 or greater. * Available since PEAR DB 1.7.0. * + new_link If set to true, causes subsequent calls to * connect() to return a new connection link * instead of the existing one. WARNING: this is * not portable to other DBMS's. * Available since PEAR DB 1.7.0. * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('oci8')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } // Backwards compatibility with DB < 1.7.0 if (empty($dsn['database']) && !empty($dsn['hostspec'])) { $db = $dsn['hostspec']; } else { $db = $dsn['database']; } if (function_exists('oci_connect')) { if (isset($dsn['new_link']) && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) { $connect_function = 'oci_new_connect'; } else { $connect_function = $persistent ? 'oci_pconnect' : 'oci_connect'; } if (isset($this->dsn['port']) && $this->dsn['port']) { $db = '//'.$db.':'.$this->dsn['port']; } $char = empty($dsn['charset']) ? null : $dsn['charset']; $this->connection = @$connect_function($dsn['username'], $dsn['password'], $db, $char); $error = OCIError(); if (!empty($error) && $error['code'] == 12541) { // Couldn't find TNS listener. Try direct connection. $this->connection = @$connect_function($dsn['username'], $dsn['password'], null, $char); } } else { $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon'; if ($db) { $this->connection = @$connect_function($dsn['username'], $dsn['password'], $db); } elseif ($dsn['username'] || $dsn['password']) { $this->connection = @$connect_function($dsn['username'], $dsn['password']); } } if (!$this->connection) { $error = OCIError(); $error = (is_array($error)) ? $error['message'] : null; return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $error); } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { if (function_exists('oci_close')) { $ret = @oci_close($this->connection); } else { $ret = @OCILogOff($this->connection); } $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * To determine how many rows of a result set get buffered using * ocisetprefetch(), see the "result_buffering" option in setOptions(). * This option was added in Release 1.7.0. * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $this->_data = array(); $this->last_parameters = array(); $this->last_query = $query; $query = $this->modifyQuery($query); $result = @OCIParse($this->connection, $query); if (!$result) { return $this->oci8RaiseError(); } if ($this->autocommit) { $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS); } else { $success = @OCIExecute($result,OCI_DEFAULT); } if (!$success) { return $this->oci8RaiseError($result); } $this->last_stmt = $result; if ($this->_checkManip($query)) { return DB_OK; } else { @ocisetprefetch($result, $this->options['result_buffering']); return $result; } } // }}} // {{{ nextResult() /** * Move the internal oracle result pointer to the next available result * * @param a valid oci8 result resource * * @access public * * @return true if a result is available otherwise return false */ function nextResult($result) { return false; } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } if ($fetchmode & DB_FETCHMODE_ASSOC) { $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $moredata) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS); } if (!$moredata) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? OCIFreeStatement($result) : false; } /** * Frees the internal resources associated with a prepared query * * @param resource $stmt the prepared statement's resource * @param bool $free_resource should the PHP resource be freed too? * Use false if you need to get data * from the result set later. * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_oci8::prepare() */ function freePrepared($stmt, $free_resource = true) { if (!is_resource($stmt)) { return false; } if ($free_resource) { @ocifreestatement($stmt); } if (isset($this->prepare_types[(int)$stmt])) { unset($this->prepare_types[(int)$stmt]); unset($this->manip_query[(int)$stmt]); unset($this->_prepared_queries[(int)$stmt]); } else { return false; } return true; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * Only works if the DB_PORTABILITY_NUMROWS portability option * is turned on. * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows(), DB_common::setOption() */ function numRows($result) { // emulate numRows for Oracle. yuck. if ($this->options['portability'] & DB_PORTABILITY_NUMROWS && $result === $this->last_stmt) { $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')'; $save_query = $this->last_query; $save_stmt = $this->last_stmt; $count = $this->query($countquery); // Restore the last query and statement. $this->last_query = $save_query; $this->last_stmt = $save_stmt; if (DB::isError($count) || DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED))) { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } return $row[0]; } return $this->raiseError(DB_ERROR_NOT_CAPABLE); } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @OCINumCols($result); if (!$cols) { return $this->oci8RaiseError($result); } return $cols; } // }}} // {{{ prepare() /** * Prepares a query for multiple execution with execute(). * * With oci8, this is emulated. * * prepare() requires a generic query as string like * INSERT INTO numbers VALUES (?, ?, ?) * . The ? characters are placeholders. * * Three types of placeholders can be used: * + ? a quoted scalar value, i.e. strings, integers * + ! value is inserted 'as is' * + & requires a file name. The file's contents get * inserted into the query (i.e. saving binary * data in a db) * * Use backslashes to escape placeholder characters if you don't want * them to be interpreted as placeholders. Example: * "UPDATE foo SET col=? WHERE col='over \& under'" * * * @param string $query the query to be prepared * * @return mixed DB statement resource on success. DB_Error on failure. * * @see DB_oci8::execute() */ function prepare($query) { $tokens = preg_split('/((? $val) { switch ($val) { case '?': $types[$token++] = DB_PARAM_SCALAR; unset($tokens[$key]); break; case '&': $types[$token++] = DB_PARAM_OPAQUE; unset($tokens[$key]); break; case '!': $types[$token++] = DB_PARAM_MISC; unset($tokens[$key]); break; default: $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val); if ($key != $binds) { $newquery .= $tokens[$key] . ':bind' . $token; } else { $newquery .= $tokens[$key]; } } } $this->last_query = $query; $newquery = $this->modifyQuery($newquery); if (!$stmt = @OCIParse($this->connection, $newquery)) { return $this->oci8RaiseError(); } $this->prepare_types[(int)$stmt] = $types; $this->manip_query[(int)$stmt] = DB::isManip($query); $this->_prepared_queries[(int)$stmt] = $newquery; return $stmt; } // }}} // {{{ execute() /** * Executes a DB statement prepared with prepare(). * * To determine how many rows of a result set get buffered using * ocisetprefetch(), see the "result_buffering" option in setOptions(). * This option was added in Release 1.7.0. * * @param resource $stmt a DB statement resource returned from prepare() * @param mixed $data array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 for non-array items or the * quantity of elements in the array. * * @return mixed returns an oic8 result resource for successful SELECT * queries, DB_OK for other successful queries. * A DB error object is returned on failure. * * @see DB_oci8::prepare() */ function &execute($stmt, $data = array()) { $data = (array)$data; $this->last_parameters = $data; $this->last_query = $this->_prepared_queries[(int)$stmt]; $this->_data = $data; $types = $this->prepare_types[(int)$stmt]; if (count($types) != count($data)) { $tmp = $this->raiseError(DB_ERROR_MISMATCH); return $tmp; } $i = 0; foreach ($data as $key => $value) { if ($types[$i] == DB_PARAM_MISC) { /* * Oracle doesn't seem to have the ability to pass a * parameter along unchanged, so strip off quotes from start * and end, plus turn two single quotes to one single quote, * in order to avoid the quotes getting escaped by * Oracle and ending up in the database. */ $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]); $data[$key] = str_replace("''", "'", $data[$key]); } elseif ($types[$i] == DB_PARAM_OPAQUE) { $fp = @fopen($data[$key], 'rb'); if (!$fp) { $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION); return $tmp; } $data[$key] = fread($fp, filesize($data[$key])); fclose($fp); } elseif ($types[$i] == DB_PARAM_SCALAR) { // Floats have to be converted to a locale-neutral // representation. if (is_float($data[$key])) { $data[$key] = $this->quoteFloat($data[$key]); } } if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) { $tmp = $this->oci8RaiseError($stmt); return $tmp; } $this->last_query = preg_replace("/:bind$i(?!\d)/", $this->quoteSmart($data[$key]), $this->last_query, 1); $i++; } if ($this->autocommit) { $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS); } else { $success = @OCIExecute($stmt, OCI_DEFAULT); } if (!$success) { $tmp = $this->oci8RaiseError($stmt); return $tmp; } $this->last_stmt = $stmt; if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) { $this->_last_query_manip = true; $this->_next_query_manip = false; $tmp = DB_OK; } else { $this->_last_query_manip = false; @ocisetprefetch($stmt, $this->options['result_buffering']); $tmp = new DB_result($this, $stmt); } return $tmp; } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { $this->autocommit = (bool)$onoff;; return DB_OK; } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { $result = @OCICommit($this->connection); if (!$result) { return $this->oci8RaiseError(); } return DB_OK; } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { $result = @OCIRollback($this->connection); if (!$result) { return $this->oci8RaiseError(); } return DB_OK; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if ($this->last_stmt === false) { return $this->oci8RaiseError(); } $result = @OCIRowCount($this->last_stmt); if ($result === false) { return $this->oci8RaiseError($this->last_stmt); } return $result; } // }}} // {{{ modifyQuery() /** * Changes a query string for various DBMS specific reasons * * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle. * * @param string $query the query string to modify * * @return string the modified query string * * @access protected */ function modifyQuery($query) { if (preg_match('/^\s*SELECT/i', $query) && !preg_match('/\sFROM\s/i', $query)) { $query .= ' FROM dual'; } return $query; } // }}} // {{{ modifyLimitQuery() /** * Adds LIMIT clauses to a query string according to current DBMS standards * * @param string $query the query to modify * @param int $from the row to start to fetching (0 = the first row) * @param int $count the numbers of rows to fetch * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return string the query string with LIMIT clauses added * * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { // Let Oracle return the name of the columns instead of // coding a "home" SQL parser if (count($params)) { $result = $this->prepare("SELECT * FROM ($query) " . 'WHERE NULL = NULL'); $tmp = $this->execute($result, $params); } else { $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL"; if (!$result = @OCIParse($this->connection, $q_fields)) { $this->last_query = $q_fields; return $this->oci8RaiseError(); } if (!@OCIExecute($result, OCI_DEFAULT)) { $this->last_query = $q_fields; return $this->oci8RaiseError($result); } } $ncols = OCINumCols($result); $cols = array(); for ( $i = 1; $i <= $ncols; $i++ ) { $cols[] = '"' . OCIColumnName($result, $i) . '"'; } $fields = implode(', ', $cols); // XXX Test that (tip by John Lim) //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) { // // Introduce the FIRST_ROWS Oracle query optimizer // $query = substr($query, strlen($match[0]), strlen($query)); // $query = "SELECT /* +FIRST_ROWS */ " . $query; //} // Construct the query // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2 // Perhaps this could be optimized with the use of Unions $query = "SELECT $fields FROM". " (SELECT rownum as linenum, $fields FROM". " ($query)". ' WHERE rownum <= '. ($from + $count) . ') WHERE linenum >= ' . ++$from; return $query; } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_oci8::createSequence(), DB_oci8::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); $repeat = 0; do { $this->expectError(DB_ERROR_NOSUCHTABLE); $result = $this->query("SELECT ${seqname}.nextval FROM dual"); $this->popExpect(); if ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { $repeat = 1; $result = $this->createSequence($seq_name); if (DB::isError($result)) { return $this->raiseError($result); } } else { $repeat = 0; } } while ($repeat); if (DB::isError($result)) { return $this->raiseError($result); } $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); return $arr[0]; } /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_oci8::nextID(), DB_oci8::dropSequence() */ function createSequence($seq_name) { return $this->query('CREATE SEQUENCE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_oci8::nextID(), DB_oci8::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP SEQUENCE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ oci8RaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_oci8::errorNative(), DB_oci8::errorCode() */ function oci8RaiseError($errno = null) { if ($errno === null) { $error = @OCIError($this->connection); return $this->raiseError($this->errorCode($error['code']), null, null, null, $error['message']); } elseif (is_resource($errno)) { $error = @OCIError($errno); return $this->raiseError($this->errorCode($error['code']), null, null, null, $error['message']); } return $this->raiseError($this->errorCode($errno)); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error code produced by the last query * * @return int the DBMS' error code. FALSE if the code could not be * determined */ function errorNative() { if (is_resource($this->last_stmt)) { $error = @OCIError($this->last_stmt); } else { $error = @OCIError($this->connection); } if (is_array($error)) { return $error['code']; } return false; } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * NOTE: flags won't contain index information. * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) { if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $res = array(); if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ $result = strtoupper($result); $q_fields = 'SELECT column_name, data_type, data_length, ' . 'nullable ' . 'FROM user_tab_columns ' . "WHERE table_name='$result' ORDER BY column_id"; $this->last_query = $q_fields; if (!$stmt = @OCIParse($this->connection, $q_fields)) { return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA); } if (!@OCIExecute($stmt, OCI_DEFAULT)) { return $this->oci8RaiseError($stmt); } $i = 0; while (@OCIFetch($stmt)) { $res[$i] = array( 'table' => $case_func($result), 'name' => $case_func(@OCIResult($stmt, 1)), 'type' => @OCIResult($stmt, 2), 'len' => @OCIResult($stmt, 3), 'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '', ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } $i++; } if ($mode) { $res['num_fields'] = $i; } @OCIFreeStatement($stmt); } else { if (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $result = $result->result; } $res = array(); if ($result === $this->last_stmt) { $count = @OCINumCols($result); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $res[$i] = array( 'table' => '', 'name' => $case_func(@OCIColumnName($result, $i+1)), 'type' => @OCIColumnType($result, $i+1), 'len' => @OCIColumnSize($result, $i+1), 'flags' => '', ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } } else { return $this->raiseError(DB_ERROR_NOT_CAPABLE); } } return $res; } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return 'SELECT table_name FROM user_tables'; case 'synonyms': return 'SELECT synonym_name FROM user_synonyms'; case 'views': return 'SELECT view_name FROM user_views'; default: return null; } } // }}} // {{{ quoteFloat() /** * Formats a float value for use within a query in a locale-independent * manner. * * @param float the float value to be quoted. * @return string the quoted string. * @see DB_common::quoteSmart() * @since Method available since release 1.7.8. */ function quoteFloat($float) { return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/odbc.php0000660000175100017510000006452111626162707013576 0ustar danielcdanielc * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: odbc.php 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's odbc extension * for interacting with databases via ODBC connections * * These methods overload the ones declared in DB_common. * * More info on ODBC errors could be found here: * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp * * @category Database * @package DB * @author Stig Bakken * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_odbc extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'odbc'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'sql92'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * NOTE: The feature set of the following drivers are different than * the default: * + solid: 'transactions' = true * + navision: 'limit' = false * * @var array */ var $features = array( 'limit' => 'emulate', 'new_link' => false, 'numrows' => true, 'pconnect' => true, 'prepare' => false, 'ssl' => false, 'transactions' => false, ); /** * A mapping of native error codes to DB error codes * @var array */ var $errorcode_map = array( '01004' => DB_ERROR_TRUNCATED, '07001' => DB_ERROR_MISMATCH, '21S01' => DB_ERROR_VALUE_COUNT_ON_ROW, '21S02' => DB_ERROR_MISMATCH, '22001' => DB_ERROR_INVALID, '22003' => DB_ERROR_INVALID_NUMBER, '22005' => DB_ERROR_INVALID_NUMBER, '22008' => DB_ERROR_INVALID_DATE, '22012' => DB_ERROR_DIVZERO, '23000' => DB_ERROR_CONSTRAINT, '23502' => DB_ERROR_CONSTRAINT_NOT_NULL, '23503' => DB_ERROR_CONSTRAINT, '23504' => DB_ERROR_CONSTRAINT, '23505' => DB_ERROR_CONSTRAINT, '24000' => DB_ERROR_INVALID, '34000' => DB_ERROR_INVALID, '37000' => DB_ERROR_SYNTAX, '42000' => DB_ERROR_SYNTAX, '42601' => DB_ERROR_SYNTAX, 'IM001' => DB_ERROR_UNSUPPORTED, 'S0000' => DB_ERROR_NOSUCHTABLE, 'S0001' => DB_ERROR_ALREADY_EXISTS, 'S0002' => DB_ERROR_NOSUCHTABLE, 'S0011' => DB_ERROR_ALREADY_EXISTS, 'S0012' => DB_ERROR_NOT_FOUND, 'S0021' => DB_ERROR_ALREADY_EXISTS, 'S0022' => DB_ERROR_NOSUCHFIELD, 'S1009' => DB_ERROR_INVALID, 'S1090' => DB_ERROR_INVALID, 'S1C00' => DB_ERROR_NOT_CAPABLE, ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * The number of rows affected by a data manipulation query * @var integer * @access private */ var $affected = 0; // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_odbc() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * PEAR DB's odbc driver supports the following extra DSN options: * + cursor The type of cursor to be used for this connection. * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('odbc')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } switch ($this->dbsyntax) { case 'access': case 'db2': case 'solid': $this->features['transactions'] = true; break; case 'navision': $this->features['limit'] = false; } /* * This is hear for backwards compatibility. Should have been using * 'database' all along, but prior to 1.6.0RC3 'hostspec' was used. */ if ($dsn['database']) { $odbcdsn = $dsn['database']; } elseif ($dsn['hostspec']) { $odbcdsn = $dsn['hostspec']; } else { $odbcdsn = 'localhost'; } $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect'; if (empty($dsn['cursor'])) { $this->connection = @$connect_function($odbcdsn, $dsn['username'], $dsn['password']); } else { $this->connection = @$connect_function($odbcdsn, $dsn['username'], $dsn['password'], $dsn['cursor']); } if (!is_resource($this->connection)) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $this->errorNative()); } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $err = @odbc_close($this->connection); $this->connection = null; return $err; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $this->last_query = $query; $query = $this->modifyQuery($query); $result = @odbc_exec($this->connection, $query); if (!$result) { return $this->odbcRaiseError(); // XXX ERRORMSG } // Determine which queries that should return data, and which // should return an error code only. if ($this->_checkManip($query)) { $this->affected = $result; // For affectedRows() return DB_OK; } $this->affected = 0; return $result; } // }}} // {{{ nextResult() /** * Move the internal odbc result pointer to the next available result * * @param a valid fbsql result resource * * @access public * * @return true if a result is available otherwise return false */ function nextResult($result) { return @odbc_next_result($result); } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { $arr = array(); if ($rownum !== null) { $rownum++; // ODBC first row is 1 if (version_compare(phpversion(), '4.2.0', 'ge')) { $cols = @odbc_fetch_into($result, $arr, $rownum); } else { $cols = @odbc_fetch_into($result, $rownum, $arr); } } else { $cols = @odbc_fetch_into($result, $arr); } if (!$cols) { return null; } if ($fetchmode !== DB_FETCHMODE_ORDERED) { for ($i = 0; $i < count($arr); $i++) { $colName = @odbc_field_name($result, $i+1); $a[$colName] = $arr[$i]; } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $a = array_change_key_case($a, CASE_LOWER); } $arr = $a; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? odbc_free_result($result) : false; } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @odbc_num_fields($result); if (!$cols) { return $this->odbcRaiseError(); } return $cols; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if (empty($this->affected)) { // In case of SELECT stms return 0; } $nrows = @odbc_num_rows($this->affected); if ($nrows == -1) { return $this->odbcRaiseError(); } return $nrows; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * Not all ODBC drivers support this functionality. If they don't * a DB_Error object for DB_ERROR_UNSUPPORTED is returned. * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($result) { $nrows = @odbc_num_rows($result); if ($nrows == -1) { return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED); } if ($nrows === false) { return $this->odbcRaiseError(); } return $nrows; } // }}} // {{{ quoteIdentifier() /** * Quotes a string so it can be safely used as a table or column name * * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked * "Use ANSI quoted identifiers" when setting up the ODBC data source. * * @param string $str identifier name to be quoted * * @return string quoted identifier string * * @see DB_common::quoteIdentifier() * @since Method available since Release 1.6.0 */ function quoteIdentifier($str) { switch ($this->dsn['dbsyntax']) { case 'access': return '[' . $str . ']'; case 'mssql': case 'sybase': return '[' . str_replace(']', ']]', $str) . ']'; case 'mysql': case 'mysqli': return '`' . $str . '`'; default: return '"' . str_replace('"', '""', $str) . '"'; } } // }}} // {{{ quote() /** * @deprecated Deprecated in release 1.6.0 * @internal */ function quote($str) { return $this->quoteSmart($str); } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_odbc::createSequence(), DB_odbc::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); $repeat = 0; do { $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query("update ${seqname} set id = id + 1"); $this->popErrorHandling(); if ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { $repeat = 1; $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->createSequence($seq_name); $this->popErrorHandling(); if (DB::isError($result)) { return $this->raiseError($result); } $result = $this->query("insert into ${seqname} (id) values(0)"); } else { $repeat = 0; } } while ($repeat); if (DB::isError($result)) { return $this->raiseError($result); } $result = $this->query("select id from ${seqname}"); if (DB::isError($result)) { return $result; } $row = $result->fetchRow(DB_FETCHMODE_ORDERED); if (DB::isError($row || !$row)) { return $row; } return $row[0]; } /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_odbc::nextID(), DB_odbc::dropSequence() */ function createSequence($seq_name) { return $this->query('CREATE TABLE ' . $this->getSequenceName($seq_name) . ' (id integer NOT NULL,' . ' PRIMARY KEY(id))'); } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_odbc::nextID(), DB_odbc::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { if (!@odbc_autocommit($this->connection, $onoff)) { return $this->odbcRaiseError(); } return DB_OK; } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { if (!@odbc_commit($this->connection)) { return $this->odbcRaiseError(); } return DB_OK; } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { if (!@odbc_rollback($this->connection)) { return $this->odbcRaiseError(); } return DB_OK; } // }}} // {{{ odbcRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_odbc::errorNative(), DB_common::errorCode() */ function odbcRaiseError($errno = null) { if ($errno === null) { switch ($this->dbsyntax) { case 'access': if ($this->options['portability'] & DB_PORTABILITY_ERRORS) { $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD; } else { // Doing this in case mode changes during runtime. $this->errorcode_map['07001'] = DB_ERROR_MISMATCH; } $native_code = odbc_error($this->connection); // S1000 is for "General Error." Let's be more specific. if ($native_code == 'S1000') { $errormsg = odbc_errormsg($this->connection); static $error_regexps; if (!isset($error_regexps)) { $error_regexps = array( '/includes related records.$/i' => DB_ERROR_CONSTRAINT, '/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL, ); } foreach ($error_regexps as $regexp => $code) { if (preg_match($regexp, $errormsg)) { return $this->raiseError($code, null, null, null, $native_code . ' ' . $errormsg); } } $errno = DB_ERROR; } else { $errno = $this->errorCode($native_code); } break; default: $errno = $this->errorCode(odbc_error($this->connection)); } } return $this->raiseError($errno, null, null, null, $this->errorNative()); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error code and message produced by the last query * * @return string the DBMS' error code and message */ function errorNative() { if (!is_resource($this->connection)) { return @odbc_error() . ' ' . @odbc_errormsg(); } return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection); } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() * @since Method available since Release 1.7.0 */ function tableInfo($result, $mode = null) { if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ $id = @odbc_exec($this->connection, "SELECT * FROM $result"); if (!$id) { return $this->odbcRaiseError(); } $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ $id = $result; $got_string = false; } if (!is_resource($id)) { return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = @odbc_num_fields($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $col = $i + 1; $res[$i] = array( 'table' => $got_string ? $case_func($result) : '', 'name' => $case_func(@odbc_field_name($id, $col)), 'type' => @odbc_field_type($id, $col), 'len' => @odbc_field_len($id, $col), 'flags' => '', ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } // free the result only if we were called on a table if ($got_string) { @odbc_free_result($id); } return $res; } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com. * * @param string $type the kind of objects you want to retrieve * * @return string the list of objects requested * * @access protected * @see DB_common::getListOf() * @since Method available since Release 1.7.0 */ function getSpecialQuery($type) { switch ($type) { case 'databases': if (!function_exists('odbc_data_source')) { return null; } $res = @odbc_data_source($this->connection, SQL_FETCH_FIRST); if (is_array($res)) { $out = array($res['server']); while($res = @odbc_data_source($this->connection, SQL_FETCH_NEXT)) { $out[] = $res['server']; } return $out; } else { return $this->odbcRaiseError(); } break; case 'tables': case 'schema.tables': $keep = 'TABLE'; break; case 'views': $keep = 'VIEW'; break; default: return null; } /* * Removing non-conforming items in the while loop rather than * in the odbc_tables() call because some backends choke on this: * odbc_tables($this->connection, '', '', '', 'TABLE') */ $res = @odbc_tables($this->connection); if (!$res) { return $this->odbcRaiseError(); } $out = array(); while ($row = odbc_fetch_array($res)) { if ($row['TABLE_TYPE'] != $keep) { continue; } if ($type == 'schema.tables') { $out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME']; } else { $out[] = $row['TABLE_NAME']; } } return $out; } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/pgsql.php0000660000175100017510000011037311626162707014012 0ustar danielcdanielc * @author Stig Bakken * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: pgsql.php 306604 2010-12-24 06:09:35Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's pgsql extension * for interacting with PostgreSQL databases * * These methods overload the ones declared in DB_common. * * @category Database * @package DB * @author Rui Hirokawa * @author Stig Bakken * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_pgsql extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'pgsql'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'pgsql'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'alter', 'new_link' => '4.3.0', 'numrows' => true, 'pconnect' => true, 'prepare' => false, 'ssl' => true, 'transactions' => true, ); /** * A mapping of native error codes to DB error codes * @var array */ var $errorcode_map = array( ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * Should data manipulation queries be committed automatically? * @var bool * @access private */ var $autocommit = true; /** * The quantity of transactions begun * * {@internal While this is private, it can't actually be designated * private in PHP 5 because it is directly accessed in the test suite.}} * * @var integer * @access private */ var $transaction_opcount = 0; /** * The number of rows affected by a data manipulation query * @var integer */ var $affected = 0; /** * The current row being looked at in fetchInto() * @var array * @access private */ var $row = array(); /** * The number of rows in a given result set * @var array * @access private */ var $_num_rows = array(); // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_pgsql() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * PEAR DB's pgsql driver supports the following extra DSN options: * + connect_timeout How many seconds to wait for a connection to * be established. Available since PEAR DB 1.7.0. * + new_link If set to true, causes subsequent calls to * connect() to return a new connection link * instead of the existing one. WARNING: this is * not portable to other DBMS's. Available only * if PHP is >= 4.3.0 and PEAR DB is >= 1.7.0. * + options Command line options to be sent to the server. * Available since PEAR DB 1.6.4. * + service Specifies a service name in pg_service.conf that * holds additional connection parameters. * Available since PEAR DB 1.7.0. * + sslmode How should SSL be used when connecting? Values: * disable, allow, prefer or require. * Available since PEAR DB 1.7.0. * + tty This was used to specify where to send server * debug output. Available since PEAR DB 1.6.4. * * Example of connecting to a new link via a socket: * * require_once 'DB.php'; * * $dsn = 'pgsql://user:pass@unix(/tmp)/dbname?new_link=true'; * $options = array( * 'portability' => DB_PORTABILITY_ALL, * ); * * $db = DB::connect($dsn, $options); * if (PEAR::isError($db)) { * die($db->getMessage()); * } * * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. * * @link http://www.postgresql.org/docs/current/static/libpq.html#LIBPQ-CONNECT */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('pgsql')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } $protocol = $dsn['protocol'] ? $dsn['protocol'] : 'tcp'; $params = array(''); if ($protocol == 'tcp') { if ($dsn['hostspec']) { $params[0] .= 'host=' . $dsn['hostspec']; } if ($dsn['port']) { $params[0] .= ' port=' . $dsn['port']; } } elseif ($protocol == 'unix') { // Allow for pg socket in non-standard locations. if ($dsn['socket']) { $params[0] .= 'host=' . $dsn['socket']; } if ($dsn['port']) { $params[0] .= ' port=' . $dsn['port']; } } if ($dsn['database']) { $params[0] .= ' dbname=\'' . addslashes($dsn['database']) . '\''; } if ($dsn['username']) { $params[0] .= ' user=\'' . addslashes($dsn['username']) . '\''; } if ($dsn['password']) { $params[0] .= ' password=\'' . addslashes($dsn['password']) . '\''; } if (!empty($dsn['options'])) { $params[0] .= ' options=' . $dsn['options']; } if (!empty($dsn['tty'])) { $params[0] .= ' tty=' . $dsn['tty']; } if (!empty($dsn['connect_timeout'])) { $params[0] .= ' connect_timeout=' . $dsn['connect_timeout']; } if (!empty($dsn['sslmode'])) { $params[0] .= ' sslmode=' . $dsn['sslmode']; } if (!empty($dsn['service'])) { $params[0] .= ' service=' . $dsn['service']; } if (isset($dsn['new_link']) && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) { if (version_compare(phpversion(), '4.3.0', '>=')) { $params[] = PGSQL_CONNECT_FORCE_NEW; } } $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect'; $ini = ini_get('track_errors'); $php_errormsg = ''; if ($ini) { $this->connection = @call_user_func_array($connect_function, $params); } else { @ini_set('track_errors', 1); $this->connection = @call_user_func_array($connect_function, $params); @ini_set('track_errors', $ini); } if (!$this->connection) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, $php_errormsg); } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @pg_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $ismanip = $this->_checkManip($query); $this->last_query = $query; $query = $this->modifyQuery($query); if (!$this->autocommit && $ismanip) { if ($this->transaction_opcount == 0) { $result = @pg_exec($this->connection, 'begin;'); if (!$result) { return $this->pgsqlRaiseError(); } } $this->transaction_opcount++; } $result = @pg_exec($this->connection, $query); if (!$result) { return $this->pgsqlRaiseError(); } /* * Determine whether queries produce affected rows, result or nothing. * * This logic was introduced in version 1.1 of the file by ssb, * though the regex has been modified slightly since then. * * PostgreSQL commands: * ABORT, ALTER, BEGIN, CLOSE, CLUSTER, COMMIT, COPY, * CREATE, DECLARE, DELETE, DROP TABLE, EXPLAIN, FETCH, * GRANT, INSERT, LISTEN, LOAD, LOCK, MOVE, NOTIFY, RESET, * REVOKE, ROLLBACK, SELECT, SELECT INTO, SET, SHOW, * UNLISTEN, UPDATE, VACUUM, WITH */ if ($ismanip) { $this->affected = @pg_affected_rows($result); return DB_OK; } elseif (preg_match('/^\s*\(*\s*(SELECT|EXPLAIN|FETCH|SHOW|WITH)\s/si', $query)) { $this->row[(int)$result] = 0; // reset the row counter. $numrows = $this->numRows($result); if (is_object($numrows)) { return $numrows; } $this->_num_rows[(int)$result] = $numrows; $this->affected = 0; return $result; } else { $this->affected = 0; return DB_OK; } } // }}} // {{{ nextResult() /** * Move the internal pgsql result pointer to the next available result * * @param a valid fbsql result resource * * @access public * * @return true if a result is available otherwise return false */ function nextResult($result) { return false; } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { $result_int = (int)$result; $rownum = ($rownum !== null) ? $rownum : $this->row[$result_int]; if ($rownum >= $this->_num_rows[$result_int]) { return null; } if ($fetchmode & DB_FETCHMODE_ASSOC) { $arr = @pg_fetch_array($result, $rownum, PGSQL_ASSOC); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $arr = @pg_fetch_row($result, $rownum); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } $this->row[$result_int] = ++$rownum; return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { if (is_resource($result)) { unset($this->row[(int)$result]); unset($this->_num_rows[(int)$result]); $this->affected = 0; return @pg_freeresult($result); } return false; } // }}} // {{{ quote() /** * @deprecated Deprecated in release 1.6.0 * @internal */ function quote($str) { return $this->quoteSmart($str); } // }}} // {{{ quoteBoolean() /** * Formats a boolean value for use within a query in a locale-independent * manner. * * @param boolean the boolean value to be quoted. * @return string the quoted string. * @see DB_common::quoteSmart() * @since Method available since release 1.7.8. */ function quoteBoolean($boolean) { return $boolean ? 'TRUE' : 'FALSE'; } // }}} // {{{ escapeSimple() /** * Escapes a string according to the current DBMS's standards * * {@internal PostgreSQL treats a backslash as an escape character, * so they are escaped as well. * * @param string $str the string to be escaped * * @return string the escaped string * * @see DB_common::quoteSmart() * @since Method available since Release 1.6.0 */ function escapeSimple($str) { if (function_exists('pg_escape_string')) { /* This fixes an undocumented BC break in PHP 5.2.0 which changed * the prototype of pg_escape_string. I'm not thrilled about having * to sniff the PHP version, quite frankly, but it's the only way * to deal with the problem. Revision 1.331.2.13.2.10 on * php-src/ext/pgsql/pgsql.c (PHP_5_2 branch) is to blame, for the * record. */ if (version_compare(PHP_VERSION, '5.2.0', '>=')) { return pg_escape_string($this->connection, $str); } else { return pg_escape_string($str); } } else { return str_replace("'", "''", str_replace('\\', '\\\\', $str)); } } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @pg_numfields($result); if (!$cols) { return $this->pgsqlRaiseError(); } return $cols; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($result) { $rows = @pg_numrows($result); if ($rows === null) { return $this->pgsqlRaiseError(); } return $rows; } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { // XXX if $this->transaction_opcount > 0, we should probably // issue a warning here. $this->autocommit = $onoff ? true : false; return DB_OK; } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { if ($this->transaction_opcount > 0) { // (disabled) hack to shut up error messages from libpq.a //@fclose(@fopen("php://stderr", "w")); $result = @pg_exec($this->connection, 'end;'); $this->transaction_opcount = 0; if (!$result) { return $this->pgsqlRaiseError(); } } return DB_OK; } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { if ($this->transaction_opcount > 0) { $result = @pg_exec($this->connection, 'abort;'); $this->transaction_opcount = 0; if (!$result) { return $this->pgsqlRaiseError(); } } return DB_OK; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { return $this->affected; } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_pgsql::createSequence(), DB_pgsql::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); $repeat = false; do { $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query("SELECT NEXTVAL('${seqname}')"); $this->popErrorHandling(); if ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { $repeat = true; $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->createSequence($seq_name); $this->popErrorHandling(); if (DB::isError($result)) { return $this->raiseError($result); } } else { $repeat = false; } } while ($repeat); if (DB::isError($result)) { return $this->raiseError($result); } $arr = $result->fetchRow(DB_FETCHMODE_ORDERED); $result->free(); return $arr[0]; } // }}} // {{{ createSequence() /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_pgsql::nextID(), DB_pgsql::dropSequence() */ function createSequence($seq_name) { $seqname = $this->getSequenceName($seq_name); $result = $this->query("CREATE SEQUENCE ${seqname}"); return $result; } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_pgsql::nextID(), DB_pgsql::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP SEQUENCE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ modifyLimitQuery() /** * Adds LIMIT clauses to a query string according to current DBMS standards * * @param string $query the query to modify * @param int $from the row to start to fetching (0 = the first row) * @param int $count the numbers of rows to fetch * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return string the query string with LIMIT clauses added * * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { return "$query LIMIT $count OFFSET $from"; } // }}} // {{{ pgsqlRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_pgsql::errorNative(), DB_pgsql::errorCode() */ function pgsqlRaiseError($errno = null) { $native = $this->errorNative(); if (!$native) { $native = 'Database connection has been lost.'; $errno = DB_ERROR_CONNECT_FAILED; } if ($errno === null) { $errno = $this->errorCode($native); } return $this->raiseError($errno, null, null, null, $native); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error message produced by the last query * * {@internal Error messages are used instead of error codes * in order to support older versions of PostgreSQL.}} * * @return string the DBMS' error message */ function errorNative() { return @pg_errormessage($this->connection); } // }}} // {{{ errorCode() /** * Determines PEAR::DB error code from the database's text error message. * * @param string $errormsg error message returned from the database * @return integer an error number from a DB error constant */ function errorCode($errormsg) { static $error_regexps; if (!isset($error_regexps)) { $error_regexps = array( '/column .* (of relation .*)?does not exist/i' => DB_ERROR_NOSUCHFIELD, '/(relation|sequence|table).*does not exist|class .* not found/i' => DB_ERROR_NOSUCHTABLE, '/index .* does not exist/' => DB_ERROR_NOT_FOUND, '/relation .* already exists/i' => DB_ERROR_ALREADY_EXISTS, '/(divide|division) by zero$/i' => DB_ERROR_DIVZERO, '/pg_atoi: error in .*: can\'t parse /i' => DB_ERROR_INVALID_NUMBER, '/invalid input syntax for( type)? (integer|numeric)/i' => DB_ERROR_INVALID_NUMBER, '/value .* is out of range for type \w*int/i' => DB_ERROR_INVALID_NUMBER, '/integer out of range/i' => DB_ERROR_INVALID_NUMBER, '/value too long for type character/i' => DB_ERROR_INVALID, '/attribute .* not found|relation .* does not have attribute/i' => DB_ERROR_NOSUCHFIELD, '/column .* specified in USING clause does not exist in (left|right) table/i' => DB_ERROR_NOSUCHFIELD, '/parser: parse error at or near/i' => DB_ERROR_SYNTAX, '/syntax error at/' => DB_ERROR_SYNTAX, '/column reference .* is ambiguous/i' => DB_ERROR_SYNTAX, '/permission denied/' => DB_ERROR_ACCESS_VIOLATION, '/violates not-null constraint/' => DB_ERROR_CONSTRAINT_NOT_NULL, '/violates [\w ]+ constraint/' => DB_ERROR_CONSTRAINT, '/referential integrity violation/' => DB_ERROR_CONSTRAINT, '/more expressions than target columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW, ); } foreach ($error_regexps as $regexp => $code) { if (preg_match($regexp, $errormsg)) { return $code; } } // Fall back to DB_ERROR if there was no mapping. return DB_ERROR; } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() */ function tableInfo($result, $mode = null) { if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ $id = @pg_exec($this->connection, "SELECT * FROM $result LIMIT 0"); $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ $id = $result; $got_string = false; } if (!is_resource($id)) { return $this->pgsqlRaiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = @pg_numfields($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $res[$i] = array( 'table' => $got_string ? $case_func($result) : '', 'name' => $case_func(@pg_fieldname($id, $i)), 'type' => @pg_fieldtype($id, $i), 'len' => @pg_fieldsize($id, $i), 'flags' => $got_string ? $this->_pgFieldFlags($id, $i, $result) : '', ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } // free the result only if we were called on a table if ($got_string) { @pg_freeresult($id); } return $res; } // }}} // {{{ _pgFieldFlags() /** * Get a column's flags * * Supports "not_null", "default_value", "primary_key", "unique_key" * and "multiple_key". The default value is passed through * rawurlencode() in case there are spaces in it. * * @param int $resource the PostgreSQL result identifier * @param int $num_field the field number * * @return string the flags * * @access private */ function _pgFieldFlags($resource, $num_field, $table_name) { $field_name = @pg_fieldname($resource, $num_field); // Check if there's a schema in $table_name and update things // accordingly. $from = 'pg_attribute f, pg_class tab, pg_type typ'; if (strpos($table_name, '.') !== false) { $from .= ', pg_namespace nsp'; list($schema, $table) = explode('.', $table_name); $tableWhere = "tab.relname = '$table' AND tab.relnamespace = nsp.oid AND nsp.nspname = '$schema'"; } else { $tableWhere = "tab.relname = '$table_name'"; } $result = @pg_exec($this->connection, "SELECT f.attnotnull, f.atthasdef FROM $from WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid AND f.attname = '$field_name' AND $tableWhere"); if (@pg_numrows($result) > 0) { $row = @pg_fetch_row($result, 0); $flags = ($row[0] == 't') ? 'not_null ' : ''; if ($row[1] == 't') { $result = @pg_exec($this->connection, "SELECT a.adsrc FROM $from, pg_attrdef a WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid AND f.attrelid = a.adrelid AND f.attname = '$field_name' AND $tableWhere AND f.attnum = a.adnum"); $row = @pg_fetch_row($result, 0); $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]); $flags .= 'default_' . rawurlencode($num) . ' '; } } else { $flags = ''; } $result = @pg_exec($this->connection, "SELECT i.indisunique, i.indisprimary, i.indkey FROM $from, pg_index i WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid AND f.attrelid = i.indrelid AND f.attname = '$field_name' AND $tableWhere"); $count = @pg_numrows($result); for ($i = 0; $i < $count ; $i++) { $row = @pg_fetch_row($result, $i); $keys = explode(' ', $row[2]); if (in_array($num_field + 1, $keys)) { $flags .= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : ''; $flags .= ($row[1] == 't') ? 'primary_key ' : ''; if (count($keys) > 1) $flags .= 'multiple_key '; } } return trim($flags); } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return 'SELECT c.relname AS "Name"' . ' FROM pg_class c, pg_user u' . ' WHERE c.relowner = u.usesysid' . " AND c.relkind = 'r'" . ' AND NOT EXISTS' . ' (SELECT 1 FROM pg_views' . ' WHERE viewname = c.relname)' . " AND c.relname !~ '^(pg_|sql_)'" . ' UNION' . ' SELECT c.relname AS "Name"' . ' FROM pg_class c' . " WHERE c.relkind = 'r'" . ' AND NOT EXISTS' . ' (SELECT 1 FROM pg_views' . ' WHERE viewname = c.relname)' . ' AND NOT EXISTS' . ' (SELECT 1 FROM pg_user' . ' WHERE usesysid = c.relowner)' . " AND c.relname !~ '^pg_'"; case 'schema.tables': return "SELECT schemaname || '.' || tablename" . ' AS "Name"' . ' FROM pg_catalog.pg_tables' . ' WHERE schemaname NOT IN' . " ('pg_catalog', 'information_schema', 'pg_toast')"; case 'schema.views': return "SELECT schemaname || '.' || viewname from pg_views WHERE schemaname" . " NOT IN ('information_schema', 'pg_catalog')"; case 'views': // Table cols: viewname | viewowner | definition return 'SELECT viewname from pg_views WHERE schemaname' . " NOT IN ('information_schema', 'pg_catalog')"; case 'users': // cols: usename |usesysid|usecreatedb|usetrace|usesuper|usecatupd|passwd |valuntil return 'SELECT usename FROM pg_user'; case 'databases': return 'SELECT datname FROM pg_database'; case 'functions': case 'procedures': return 'SELECT proname FROM pg_proc WHERE proowner <> 1'; default: return null; } } // }}} // {{{ _checkManip() /** * Checks if the given query is a manipulation query. This also takes into * account the _next_query_manip flag and sets the _last_query_manip flag * (and resets _next_query_manip) according to the result. * * @param string The query to check. * * @return boolean true if the query is a manipulation query, false * otherwise * * @access protected */ function _checkManip($query) { return (preg_match('/^\s*(SAVEPOINT|RELEASE)\s+/i', $query) || parent::_checkManip($query)); } } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/sqlite.php0000660000175100017510000007366011626162707014174 0ustar danielcdanielc * @author Mika Tuupola * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 * @version CVS: $Id: sqlite.php 306605 2010-12-24 06:22:59Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's sqlite extension * for interacting with SQLite databases * * These methods overload the ones declared in DB_common. * * NOTICE: This driver needs PHP's track_errors ini setting to be on. * It is automatically turned on when connecting to the database. * Make sure your scripts don't turn it off. * * @category Database * @package DB * @author Urs Gehrig * @author Mika Tuupola * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_sqlite extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'sqlite'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'sqlite'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'alter', 'new_link' => false, 'numrows' => true, 'pconnect' => true, 'prepare' => false, 'ssl' => false, 'transactions' => false, ); /** * A mapping of native error codes to DB error codes * * {@internal Error codes according to sqlite_exec. See the online * manual at http://sqlite.org/c_interface.html for info. * This error handling based on sqlite_exec is not yet implemented.}} * * @var array */ var $errorcode_map = array( ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * SQLite data types * * @link http://www.sqlite.org/datatypes.html * * @var array */ var $keywords = array ( 'BLOB' => '', 'BOOLEAN' => '', 'CHARACTER' => '', 'CLOB' => '', 'FLOAT' => '', 'INTEGER' => '', 'KEY' => '', 'NATIONAL' => '', 'NUMERIC' => '', 'NVARCHAR' => '', 'PRIMARY' => '', 'TEXT' => '', 'TIMESTAMP' => '', 'UNIQUE' => '', 'VARCHAR' => '', 'VARYING' => '', ); /** * The most recent error message from $php_errormsg * @var string * @access private */ var $_lasterror = ''; // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_sqlite() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * PEAR DB's sqlite driver supports the following extra DSN options: * + mode The permissions for the database file, in four digit * chmod octal format (eg "0600"). * * Example of connecting to a database in read-only mode: * * require_once 'DB.php'; * * $dsn = 'sqlite:///path/and/name/of/db/file?mode=0400'; * $options = array( * 'portability' => DB_PORTABILITY_ALL, * ); * * $db = DB::connect($dsn, $options); * if (PEAR::isError($db)) { * die($db->getMessage()); * } * * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('sqlite')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } if (!$dsn['database']) { return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); } if ($dsn['database'] !== ':memory:') { if (!file_exists($dsn['database'])) { if (!touch($dsn['database'])) { return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); } if (!isset($dsn['mode']) || !is_numeric($dsn['mode'])) { $mode = 0644; } else { $mode = octdec($dsn['mode']); } if (!chmod($dsn['database'], $mode)) { return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); } if (!file_exists($dsn['database'])) { return $this->sqliteRaiseError(DB_ERROR_NOT_FOUND); } } if (!is_file($dsn['database'])) { return $this->sqliteRaiseError(DB_ERROR_INVALID); } if (!is_readable($dsn['database'])) { return $this->sqliteRaiseError(DB_ERROR_ACCESS_VIOLATION); } } $connect_function = $persistent ? 'sqlite_popen' : 'sqlite_open'; // track_errors must remain on for simpleQuery() @ini_set('track_errors', 1); $php_errormsg = ''; if (!$this->connection = @$connect_function($dsn['database'])) { return $this->raiseError(DB_ERROR_NODBSELECTED, null, null, null, $php_errormsg); } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @sqlite_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * NOTICE: This method needs PHP's track_errors ini setting to be on. * It is automatically turned on when connecting to the database. * Make sure your scripts don't turn it off. * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $ismanip = $this->_checkManip($query); $this->last_query = $query; $query = $this->modifyQuery($query); $php_errormsg = ''; $result = @sqlite_query($query, $this->connection); $this->_lasterror = $php_errormsg ? $php_errormsg : ''; $this->result = $result; if (!$this->result) { return $this->sqliteRaiseError(null); } // sqlite_query() seems to allways return a resource // so cant use that. Using $ismanip instead if (!$ismanip) { $numRows = $this->numRows($result); if (is_object($numRows)) { // we've got PEAR_Error return $numRows; } return $result; } return DB_OK; } // }}} // {{{ nextResult() /** * Move the internal sqlite result pointer to the next available result * * @param resource $result the valid sqlite result resource * * @return bool true if a result is available otherwise return false */ function nextResult($result) { return false; } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@sqlite_seek($this->result, $rownum)) { return null; } } if ($fetchmode & DB_FETCHMODE_ASSOC) { $arr = @sqlite_fetch_array($result, SQLITE_ASSOC); if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } /* Remove extraneous " characters from the fields in the result. * Fixes bug #11716. */ if (is_array($arr) && count($arr) > 0) { $strippedArr = array(); foreach ($arr as $field => $value) { $strippedArr[trim($field, '"')] = $value; } $arr = $strippedArr; } } else { $arr = @sqlite_fetch_array($result, SQLITE_NUM); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { /* * Even though this DBMS already trims output, we do this because * a field might have intentional whitespace at the end that * gets removed by DB_PORTABILITY_RTRIM under another driver. */ $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult(&$result) { // XXX No native free? if (!is_resource($result)) { return false; } $result = null; return true; } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @sqlite_num_fields($result); if (!$cols) { return $this->sqliteRaiseError(); } return $cols; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($result) { $rows = @sqlite_num_rows($result); if ($rows === null) { return $this->sqliteRaiseError(); } return $rows; } // }}} // {{{ affected() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { return @sqlite_changes($this->connection); } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_sqlite::nextID(), DB_sqlite::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_sqlite::nextID(), DB_sqlite::dropSequence() */ function createSequence($seq_name) { $seqname = $this->getSequenceName($seq_name); $query = 'CREATE TABLE ' . $seqname . ' (id INTEGER UNSIGNED PRIMARY KEY) '; $result = $this->query($query); if (DB::isError($result)) { return($result); } $query = "CREATE TRIGGER ${seqname}_cleanup AFTER INSERT ON $seqname BEGIN DELETE FROM $seqname WHERE idquery($query); if (DB::isError($result)) { return($result); } } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_sqlite::createSequence(), DB_sqlite::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); do { $repeat = 0; $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query("INSERT INTO $seqname (id) VALUES (NULL)"); $this->popErrorHandling(); if ($result === DB_OK) { $id = @sqlite_last_insert_rowid($this->connection); if ($id != 0) { return $id; } } elseif ($ondemand && DB::isError($result) && $result->getCode() == DB_ERROR_NOSUCHTABLE) { $result = $this->createSequence($seq_name); if (DB::isError($result)) { return $this->raiseError($result); } else { $repeat = 1; } } } while ($repeat); return $this->raiseError($result); } // }}} // {{{ getDbFileStats() /** * Get the file stats for the current database * * Possible arguments are dev, ino, mode, nlink, uid, gid, rdev, size, * atime, mtime, ctime, blksize, blocks or a numeric key between * 0 and 12. * * @param string $arg the array key for stats() * * @return mixed an array on an unspecified key, integer on a passed * arg and false at a stats error */ function getDbFileStats($arg = '') { $stats = stat($this->dsn['database']); if ($stats == false) { return false; } if (is_array($stats)) { if (is_numeric($arg)) { if (((int)$arg <= 12) & ((int)$arg >= 0)) { return false; } return $stats[$arg ]; } if (array_key_exists(trim($arg), $stats)) { return $stats[$arg ]; } } return $stats; } // }}} // {{{ escapeSimple() /** * Escapes a string according to the current DBMS's standards * * In SQLite, this makes things safe for inserts/updates, but may * cause problems when performing text comparisons against columns * containing binary data. See the * {@link http://php.net/sqlite_escape_string PHP manual} for more info. * * @param string $str the string to be escaped * * @return string the escaped string * * @since Method available since Release 1.6.1 * @see DB_common::escapeSimple() */ function escapeSimple($str) { return @sqlite_escape_string($str); } // }}} // {{{ modifyLimitQuery() /** * Adds LIMIT clauses to a query string according to current DBMS standards * * @param string $query the query to modify * @param int $from the row to start to fetching (0 = the first row) * @param int $count the numbers of rows to fetch * @param mixed $params array, string or numeric data to be used in * execution of the statement. Quantity of items * passed must match quantity of placeholders in * query: meaning 1 placeholder for non-array * parameters or 1 placeholder per array element. * * @return string the query string with LIMIT clauses added * * @access protected */ function modifyLimitQuery($query, $from, $count, $params = array()) { return "$query LIMIT $count OFFSET $from"; } // }}} // {{{ modifyQuery() /** * Changes a query string for various DBMS specific reasons * * This little hack lets you know how many rows were deleted * when running a "DELETE FROM table" query. Only implemented * if the DB_PORTABILITY_DELETE_COUNT portability option is on. * * @param string $query the query string to modify * * @return string the modified query string * * @access protected * @see DB_common::setOption() */ function modifyQuery($query) { if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) { if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', 'DELETE FROM \1 WHERE 1=1', $query); } } return $query; } // }}} // {{{ sqliteRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_sqlite::errorNative(), DB_sqlite::errorCode() */ function sqliteRaiseError($errno = null) { $native = $this->errorNative(); if ($errno === null) { $errno = $this->errorCode($native); } $errorcode = @sqlite_last_error($this->connection); $userinfo = "$errorcode ** $this->last_query"; return $this->raiseError($errno, null, null, $userinfo, $native); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error message produced by the last query * * {@internal This is used to retrieve more meaningfull error messages * because sqlite_last_error() does not provide adequate info.}} * * @return string the DBMS' error message */ function errorNative() { return $this->_lasterror; } // }}} // {{{ errorCode() /** * Determines PEAR::DB error code from the database's text error message * * @param string $errormsg the error message returned from the database * * @return integer the DB error number */ function errorCode($errormsg) { static $error_regexps; // PHP 5.2+ prepends the function name to $php_errormsg, so we need // this hack to work around it, per bug #9599. $errormsg = preg_replace('/^sqlite[a-z_]+\(\): /', '', $errormsg); if (!isset($error_regexps)) { $error_regexps = array( '/^no such table:/' => DB_ERROR_NOSUCHTABLE, '/^no such index:/' => DB_ERROR_NOT_FOUND, '/^(table|index) .* already exists$/' => DB_ERROR_ALREADY_EXISTS, '/PRIMARY KEY must be unique/i' => DB_ERROR_CONSTRAINT, '/is not unique/' => DB_ERROR_CONSTRAINT, '/columns .* are not unique/i' => DB_ERROR_CONSTRAINT, '/uniqueness constraint failed/' => DB_ERROR_CONSTRAINT, '/may not be NULL/' => DB_ERROR_CONSTRAINT_NOT_NULL, '/^no such column:/' => DB_ERROR_NOSUCHFIELD, '/no column named/' => DB_ERROR_NOSUCHFIELD, '/column not present in both tables/i' => DB_ERROR_NOSUCHFIELD, '/^near ".*": syntax error$/' => DB_ERROR_SYNTAX, '/[0-9]+ values for [0-9]+ columns/i' => DB_ERROR_VALUE_COUNT_ON_ROW, ); } foreach ($error_regexps as $regexp => $code) { if (preg_match($regexp, $errormsg)) { return $code; } } // Fall back to DB_ERROR if there was no mapping. return DB_ERROR; } // }}} // {{{ tableInfo() /** * Returns information about a table * * @param string $result a string containing the name of a table * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() * @since Method available since Release 1.7.0 */ function tableInfo($result, $mode = null) { if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ $id = @sqlite_array_query($this->connection, "PRAGMA table_info('$result');", SQLITE_ASSOC); $got_string = true; } else { $this->last_query = ''; return $this->raiseError(DB_ERROR_NOT_CAPABLE, null, null, null, 'This DBMS can not obtain tableInfo' . ' from result sets'); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = count($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { if (strpos($id[$i]['type'], '(') !== false) { $bits = explode('(', $id[$i]['type']); $type = $bits[0]; $len = rtrim($bits[1],')'); } else { $type = $id[$i]['type']; $len = 0; } $flags = ''; if ($id[$i]['pk']) { $flags .= 'primary_key '; if (strtoupper($type) == 'INTEGER') { $flags .= 'auto_increment '; } } if ($id[$i]['notnull']) { $flags .= 'not_null '; } if ($id[$i]['dflt_value'] !== null) { $flags .= 'default_' . rawurlencode($id[$i]['dflt_value']); } $flags = trim($flags); $res[$i] = array( 'table' => $case_func($result), 'name' => $case_func($id[$i]['name']), 'type' => $type, 'len' => $len, 'flags' => $flags, ); if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } return $res; } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * @param array $args SQLITE DRIVER ONLY: a private array of arguments * used by the getSpecialQuery(). Do not use * this directly. * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type, $args = array()) { if (!is_array($args)) { return $this->raiseError('no key specified', null, null, null, 'Argument has to be an array.'); } switch ($type) { case 'master': return 'SELECT * FROM sqlite_master;'; case 'tables': return "SELECT name FROM sqlite_master WHERE type='table' " . 'UNION ALL SELECT name FROM sqlite_temp_master ' . "WHERE type='table' ORDER BY name;"; case 'schema': return 'SELECT sql FROM (SELECT * FROM sqlite_master ' . 'UNION ALL SELECT * FROM sqlite_temp_master) ' . "WHERE type!='meta' " . 'ORDER BY tbl_name, type DESC, name;'; case 'schemax': case 'schema_x': /* * Use like: * $res = $db->query($db->getSpecialQuery('schema_x', * array('table' => 'table3'))); */ return 'SELECT sql FROM (SELECT * FROM sqlite_master ' . 'UNION ALL SELECT * FROM sqlite_temp_master) ' . "WHERE tbl_name LIKE '{$args['table']}' " . "AND type!='meta' " . 'ORDER BY type DESC, name;'; case 'alter': /* * SQLite does not support ALTER TABLE; this is a helper query * to handle this. 'table' represents the table name, 'rows' * the news rows to create, 'save' the row(s) to keep _with_ * the data. * * Use like: * $args = array( * 'table' => $table, * 'rows' => "id INTEGER PRIMARY KEY, firstname TEXT, surname TEXT, datetime TEXT", * 'save' => "NULL, titel, content, datetime" * ); * $res = $db->query( $db->getSpecialQuery('alter', $args)); */ $rows = strtr($args['rows'], $this->keywords); $q = array( 'BEGIN TRANSACTION', "CREATE TEMPORARY TABLE {$args['table']}_backup ({$args['rows']})", "INSERT INTO {$args['table']}_backup SELECT {$args['save']} FROM {$args['table']}", "DROP TABLE {$args['table']}", "CREATE TABLE {$args['table']} ({$args['rows']})", "INSERT INTO {$args['table']} SELECT {$rows} FROM {$args['table']}_backup", "DROP TABLE {$args['table']}_backup", 'COMMIT', ); /* * This is a dirty hack, since the above query will not get * executed with a single query call so here the query method * will be called directly and return a select instead. */ foreach ($q as $query) { $this->query($query); } return "SELECT * FROM {$args['table']};"; default: return null; } } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/storage.php0000660000175100017510000003506511626162707014334 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: storage.php 241120 2007-08-12 05:27:25Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB class so it can be extended from */ require_once 'DB.php'; /** * Provides an object interface to a table row * * It lets you add, delete and change rows using objects rather than SQL * statements. * * @category Database * @package DB * @author Stig Bakken * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_storage extends PEAR { // {{{ properties /** the name of the table (or view, if the backend database supports updates in views) we hold data from */ var $_table = null; /** which column(s) in the table contains primary keys, can be a string for single-column primary keys, or an array of strings for multiple-column primary keys */ var $_keycolumn = null; /** DB connection handle used for all transactions */ var $_dbh = null; /** an assoc with the names of database fields stored as properties in this object */ var $_properties = array(); /** an assoc with the names of the properties in this object that have been changed since they were fetched from the database */ var $_changes = array(); /** flag that decides if data in this object can be changed. objects that don't have their table's key column in their property lists will be flagged as read-only. */ var $_readonly = false; /** function or method that implements a validator for fields that are set, this validator function returns true if the field is valid, false if not */ var $_validator = null; // }}} // {{{ constructor /** * Constructor * * @param $table string the name of the database table * * @param $keycolumn mixed string with name of key column, or array of * strings if the table has a primary key of more than one column * * @param $dbh object database connection object * * @param $validator mixed function or method used to validate * each new value, called with three parameters: the name of the * field/column that is changing, a reference to the new value and * a reference to this object * */ function DB_storage($table, $keycolumn, &$dbh, $validator = null) { $this->PEAR('DB_Error'); $this->_table = $table; $this->_keycolumn = $keycolumn; $this->_dbh = $dbh; $this->_readonly = false; $this->_validator = $validator; } // }}} // {{{ _makeWhere() /** * Utility method to build a "WHERE" clause to locate ourselves in * the table. * * XXX future improvement: use rowids? * * @access private */ function _makeWhere($keyval = null) { if (is_array($this->_keycolumn)) { if ($keyval === null) { for ($i = 0; $i < sizeof($this->_keycolumn); $i++) { $keyval[] = $this->{$this->_keycolumn[$i]}; } } $whereclause = ''; for ($i = 0; $i < sizeof($this->_keycolumn); $i++) { if ($i > 0) { $whereclause .= ' AND '; } $whereclause .= $this->_keycolumn[$i]; if (is_null($keyval[$i])) { // there's not much point in having a NULL key, // but we support it anyway $whereclause .= ' IS NULL'; } else { $whereclause .= ' = ' . $this->_dbh->quote($keyval[$i]); } } } else { if ($keyval === null) { $keyval = @$this->{$this->_keycolumn}; } $whereclause = $this->_keycolumn; if (is_null($keyval)) { // there's not much point in having a NULL key, // but we support it anyway $whereclause .= ' IS NULL'; } else { $whereclause .= ' = ' . $this->_dbh->quote($keyval); } } return $whereclause; } // }}} // {{{ setup() /** * Method used to initialize a DB_storage object from the * configured table. * * @param $keyval mixed the key[s] of the row to fetch (string or array) * * @return int DB_OK on success, a DB error if not */ function setup($keyval) { $whereclause = $this->_makeWhere($keyval); $query = 'SELECT * FROM ' . $this->_table . ' WHERE ' . $whereclause; $sth = $this->_dbh->query($query); if (DB::isError($sth)) { return $sth; } $row = $sth->fetchRow(DB_FETCHMODE_ASSOC); if (DB::isError($row)) { return $row; } if (!$row) { return $this->raiseError(null, DB_ERROR_NOT_FOUND, null, null, $query, null, true); } foreach ($row as $key => $value) { $this->_properties[$key] = true; $this->$key = $value; } return DB_OK; } // }}} // {{{ insert() /** * Create a new (empty) row in the configured table for this * object. */ function insert($newpk) { if (is_array($this->_keycolumn)) { $primarykey = $this->_keycolumn; } else { $primarykey = array($this->_keycolumn); } settype($newpk, "array"); for ($i = 0; $i < sizeof($primarykey); $i++) { $pkvals[] = $this->_dbh->quote($newpk[$i]); } $sth = $this->_dbh->query("INSERT INTO $this->_table (" . implode(",", $primarykey) . ") VALUES(" . implode(",", $pkvals) . ")"); if (DB::isError($sth)) { return $sth; } if (sizeof($newpk) == 1) { $newpk = $newpk[0]; } $this->setup($newpk); } // }}} // {{{ toString() /** * Output a simple description of this DB_storage object. * @return string object description */ function toString() { $info = strtolower(get_class($this)); $info .= " (table="; $info .= $this->_table; $info .= ", keycolumn="; if (is_array($this->_keycolumn)) { $info .= "(" . implode(",", $this->_keycolumn) . ")"; } else { $info .= $this->_keycolumn; } $info .= ", dbh="; if (is_object($this->_dbh)) { $info .= $this->_dbh->toString(); } else { $info .= "null"; } $info .= ")"; if (sizeof($this->_properties)) { $info .= " [loaded, key="; $keyname = $this->_keycolumn; if (is_array($keyname)) { $info .= "("; for ($i = 0; $i < sizeof($keyname); $i++) { if ($i > 0) { $info .= ","; } $info .= $this->$keyname[$i]; } $info .= ")"; } else { $info .= $this->$keyname; } $info .= "]"; } if (sizeof($this->_changes)) { $info .= " [modified]"; } return $info; } // }}} // {{{ dump() /** * Dump the contents of this object to "standard output". */ function dump() { foreach ($this->_properties as $prop => $foo) { print "$prop = "; print htmlentities($this->$prop); print "
\n"; } } // }}} // {{{ &create() /** * Static method used to create new DB storage objects. * @param $data assoc. array where the keys are the names * of properties/columns * @return object a new instance of DB_storage or a subclass of it */ function &create($table, &$data) { $classname = strtolower(get_class($this)); $obj = new $classname($table); foreach ($data as $name => $value) { $obj->_properties[$name] = true; $obj->$name = &$value; } return $obj; } // }}} // {{{ loadFromQuery() /** * Loads data into this object from the given query. If this * object already contains table data, changes will be saved and * the object re-initialized first. * * @param $query SQL query * * @param $params parameter list in case you want to use * prepare/execute mode * * @return int DB_OK on success, DB_WARNING_READ_ONLY if the * returned object is read-only (because the object's specified * key column was not found among the columns returned by $query), * or another DB error code in case of errors. */ // XXX commented out for now /* function loadFromQuery($query, $params = null) { if (sizeof($this->_properties)) { if (sizeof($this->_changes)) { $this->store(); $this->_changes = array(); } $this->_properties = array(); } $rowdata = $this->_dbh->getRow($query, DB_FETCHMODE_ASSOC, $params); if (DB::isError($rowdata)) { return $rowdata; } reset($rowdata); $found_keycolumn = false; while (list($key, $value) = each($rowdata)) { if ($key == $this->_keycolumn) { $found_keycolumn = true; } $this->_properties[$key] = true; $this->$key = &$value; unset($value); // have to unset, or all properties will // refer to the same value } if (!$found_keycolumn) { $this->_readonly = true; return DB_WARNING_READ_ONLY; } return DB_OK; } */ // }}} // {{{ set() /** * Modify an attriute value. */ function set($property, $newvalue) { // only change if $property is known and object is not // read-only if ($this->_readonly) { return $this->raiseError(null, DB_WARNING_READ_ONLY, null, null, null, null, true); } if (@isset($this->_properties[$property])) { if (empty($this->_validator)) { $valid = true; } else { $valid = @call_user_func($this->_validator, $this->_table, $property, $newvalue, $this->$property, $this); } if ($valid) { $this->$property = $newvalue; if (empty($this->_changes[$property])) { $this->_changes[$property] = 0; } else { $this->_changes[$property]++; } } else { return $this->raiseError(null, DB_ERROR_INVALID, null, null, "invalid field: $property", null, true); } return true; } return $this->raiseError(null, DB_ERROR_NOSUCHFIELD, null, null, "unknown field: $property", null, true); } // }}} // {{{ &get() /** * Fetch an attribute value. * * @param string attribute name * * @return attribute contents, or null if the attribute name is * unknown */ function &get($property) { // only return if $property is known if (isset($this->_properties[$property])) { return $this->$property; } $tmp = null; return $tmp; } // }}} // {{{ _DB_storage() /** * Destructor, calls DB_storage::store() if there are changes * that are to be kept. */ function _DB_storage() { if (sizeof($this->_changes)) { $this->store(); } $this->_properties = array(); $this->_changes = array(); $this->_table = null; } // }}} // {{{ store() /** * Stores changes to this object in the database. * * @return DB_OK or a DB error */ function store() { $params = array(); $vars = array(); foreach ($this->_changes as $name => $foo) { $params[] = &$this->$name; $vars[] = $name . ' = ?'; } if ($vars) { $query = 'UPDATE ' . $this->_table . ' SET ' . implode(', ', $vars) . ' WHERE ' . $this->_makeWhere(); $stmt = $this->_dbh->prepare($query); $res = $this->_dbh->execute($stmt, $params); if (DB::isError($res)) { return $res; } $this->_changes = array(); } return DB_OK; } // }}} // {{{ remove() /** * Remove the row represented by this object from the database. * * @return mixed DB_OK or a DB error */ function remove() { if ($this->_readonly) { return $this->raiseError(null, DB_WARNING_READ_ONLY, null, null, null, null, true); } $query = 'DELETE FROM ' . $this->_table .' WHERE '. $this->_makeWhere(); $res = $this->_dbh->query($query); if (DB::isError($res)) { return $res; } foreach ($this->_properties as $prop => $foo) { unset($this->$prop); } $this->_properties = array(); $this->_changes = array(); return DB_OK; } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/DB/sybase.php0000660000175100017510000007124111626162707014152 0ustar danielcdanielc * @author Antônio Carlos Venâncio Júnior * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: sybase.php 242771 2007-09-21 13:40:42Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Obtain the DB_common class so it can be extended from */ require_once 'DB/common.php'; /** * The methods PEAR DB uses to interact with PHP's sybase extension * for interacting with Sybase databases * * These methods overload the ones declared in DB_common. * * WARNING: This driver may fail with multiple connections under the * same user/pass/host and different databases. * * @category Database * @package DB * @author Sterling Hughes * @author Antônio Carlos Venâncio Júnior * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_sybase extends DB_common { // {{{ properties /** * The DB driver type (mysql, oci8, odbc, etc.) * @var string */ var $phptype = 'sybase'; /** * The database syntax variant to be used (db2, access, etc.), if any * @var string */ var $dbsyntax = 'sybase'; /** * The capabilities of this DB implementation * * The 'new_link' element contains the PHP version that first provided * new_link support for this DBMS. Contains false if it's unsupported. * * Meaning of the 'limit' element: * + 'emulate' = emulate with fetch row by number * + 'alter' = alter the query * + false = skip rows * * @var array */ var $features = array( 'limit' => 'emulate', 'new_link' => false, 'numrows' => true, 'pconnect' => true, 'prepare' => false, 'ssl' => false, 'transactions' => true, ); /** * A mapping of native error codes to DB error codes * @var array */ var $errorcode_map = array( ); /** * The raw database connection created by PHP * @var resource */ var $connection; /** * The DSN information for connecting to a database * @var array */ var $dsn = array(); /** * Should data manipulation queries be committed automatically? * @var bool * @access private */ var $autocommit = true; /** * The quantity of transactions begun * * {@internal While this is private, it can't actually be designated * private in PHP 5 because it is directly accessed in the test suite.}} * * @var integer * @access private */ var $transaction_opcount = 0; /** * The database specified in the DSN * * It's a fix to allow calls to different databases in the same script. * * @var string * @access private */ var $_db = ''; // }}} // {{{ constructor /** * This constructor calls $this->DB_common() * * @return void */ function DB_sybase() { $this->DB_common(); } // }}} // {{{ connect() /** * Connect to the database server, log in and open the database * * Don't call this method directly. Use DB::connect() instead. * * PEAR DB's sybase driver supports the following extra DSN options: * + appname The application name to use on this connection. * Available since PEAR DB 1.7.0. * + charset The character set to use on this connection. * Available since PEAR DB 1.7.0. * * @param array $dsn the data source name * @param bool $persistent should the connection be persistent? * * @return int DB_OK on success. A DB_Error object on failure. */ function connect($dsn, $persistent = false) { if (!PEAR::loadExtension('sybase') && !PEAR::loadExtension('sybase_ct')) { return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND); } $this->dsn = $dsn; if ($dsn['dbsyntax']) { $this->dbsyntax = $dsn['dbsyntax']; } $dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost'; $dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false; $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false; $dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false; $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect'; if ($dsn['username']) { $this->connection = @$connect_function($dsn['hostspec'], $dsn['username'], $dsn['password'], $dsn['charset'], $dsn['appname']); } else { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, 'The DSN did not contain a username.'); } if (!$this->connection) { return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null, null, @sybase_get_last_message()); } if ($dsn['database']) { if (!@sybase_select_db($dsn['database'], $this->connection)) { return $this->raiseError(DB_ERROR_NODBSELECTED, null, null, null, @sybase_get_last_message()); } $this->_db = $dsn['database']; } return DB_OK; } // }}} // {{{ disconnect() /** * Disconnects from the database server * * @return bool TRUE on success, FALSE on failure */ function disconnect() { $ret = @sybase_close($this->connection); $this->connection = null; return $ret; } // }}} // {{{ simpleQuery() /** * Sends a query to the database server * * @param string the SQL query string * * @return mixed + a PHP result resrouce for successful SELECT queries * + the DB_OK constant for other successful queries * + a DB_Error object on failure */ function simpleQuery($query) { $ismanip = $this->_checkManip($query); $this->last_query = $query; if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $query = $this->modifyQuery($query); if (!$this->autocommit && $ismanip) { if ($this->transaction_opcount == 0) { $result = @sybase_query('BEGIN TRANSACTION', $this->connection); if (!$result) { return $this->sybaseRaiseError(); } } $this->transaction_opcount++; } $result = @sybase_query($query, $this->connection); if (!$result) { return $this->sybaseRaiseError(); } if (is_resource($result)) { return $result; } // Determine which queries that should return data, and which // should return an error code only. return $ismanip ? DB_OK : $result; } // }}} // {{{ nextResult() /** * Move the internal sybase result pointer to the next available result * * @param a valid sybase result resource * * @access public * * @return true if a result is available otherwise return false */ function nextResult($result) { return false; } // }}} // {{{ fetchInto() /** * Places a row from the result set into the given array * * Formating of the array and the data therein are configurable. * See DB_result::fetchInto() for more information. * * This method is not meant to be called directly. Use * DB_result::fetchInto() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result the query result resource * @param array $arr the referenced array to put the data in * @param int $fetchmode how the resulting array should be indexed * @param int $rownum the row number to fetch (0 = first row) * * @return mixed DB_OK on success, NULL when the end of a result set is * reached or on failure * * @see DB_result::fetchInto() */ function fetchInto($result, &$arr, $fetchmode, $rownum = null) { if ($rownum !== null) { if (!@sybase_data_seek($result, $rownum)) { return null; } } if ($fetchmode & DB_FETCHMODE_ASSOC) { if (function_exists('sybase_fetch_assoc')) { $arr = @sybase_fetch_assoc($result); } else { if ($arr = @sybase_fetch_array($result)) { foreach ($arr as $key => $value) { if (is_int($key)) { unset($arr[$key]); } } } } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) { $arr = array_change_key_case($arr, CASE_LOWER); } } else { $arr = @sybase_fetch_row($result); } if (!$arr) { return null; } if ($this->options['portability'] & DB_PORTABILITY_RTRIM) { $this->_rtrimArrayValues($arr); } if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) { $this->_convertNullArrayValuesToEmpty($arr); } return DB_OK; } // }}} // {{{ freeResult() /** * Deletes the result set and frees the memory occupied by the result set * * This method is not meant to be called directly. Use * DB_result::free() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return bool TRUE on success, FALSE if $result is invalid * * @see DB_result::free() */ function freeResult($result) { return is_resource($result) ? sybase_free_result($result) : false; } // }}} // {{{ numCols() /** * Gets the number of columns in a result set * * This method is not meant to be called directly. Use * DB_result::numCols() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of columns. A DB_Error object on failure. * * @see DB_result::numCols() */ function numCols($result) { $cols = @sybase_num_fields($result); if (!$cols) { return $this->sybaseRaiseError(); } return $cols; } // }}} // {{{ numRows() /** * Gets the number of rows in a result set * * This method is not meant to be called directly. Use * DB_result::numRows() instead. It can't be declared "protected" * because DB_result is a separate object. * * @param resource $result PHP's query result resource * * @return int the number of rows. A DB_Error object on failure. * * @see DB_result::numRows() */ function numRows($result) { $rows = @sybase_num_rows($result); if ($rows === false) { return $this->sybaseRaiseError(); } return $rows; } // }}} // {{{ affectedRows() /** * Determines the number of rows affected by a data maniuplation query * * 0 is returned for queries that don't manipulate data. * * @return int the number of rows. A DB_Error object on failure. */ function affectedRows() { if ($this->_last_query_manip) { $result = @sybase_affected_rows($this->connection); } else { $result = 0; } return $result; } // }}} // {{{ nextId() /** * Returns the next free id in a sequence * * @param string $seq_name name of the sequence * @param boolean $ondemand when true, the seqence is automatically * created if it does not exist * * @return int the next id number in the sequence. * A DB_Error object on failure. * * @see DB_common::nextID(), DB_common::getSequenceName(), * DB_sybase::createSequence(), DB_sybase::dropSequence() */ function nextId($seq_name, $ondemand = true) { $seqname = $this->getSequenceName($seq_name); if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $repeat = 0; do { $this->pushErrorHandling(PEAR_ERROR_RETURN); $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)"); $this->popErrorHandling(); if ($ondemand && DB::isError($result) && ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE)) { $repeat = 1; $result = $this->createSequence($seq_name); if (DB::isError($result)) { return $this->raiseError($result); } } elseif (!DB::isError($result)) { $result = $this->query("SELECT @@IDENTITY FROM $seqname"); $repeat = 0; } else { $repeat = false; } } while ($repeat); if (DB::isError($result)) { return $this->raiseError($result); } $result = $result->fetchRow(DB_FETCHMODE_ORDERED); return $result[0]; } /** * Creates a new sequence * * @param string $seq_name name of the new sequence * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::createSequence(), DB_common::getSequenceName(), * DB_sybase::nextID(), DB_sybase::dropSequence() */ function createSequence($seq_name) { return $this->query('CREATE TABLE ' . $this->getSequenceName($seq_name) . ' (id numeric(10, 0) IDENTITY NOT NULL,' . ' vapor int NULL)'); } // }}} // {{{ dropSequence() /** * Deletes a sequence * * @param string $seq_name name of the sequence to be deleted * * @return int DB_OK on success. A DB_Error object on failure. * * @see DB_common::dropSequence(), DB_common::getSequenceName(), * DB_sybase::nextID(), DB_sybase::createSequence() */ function dropSequence($seq_name) { return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)); } // }}} // {{{ quoteFloat() /** * Formats a float value for use within a query in a locale-independent * manner. * * @param float the float value to be quoted. * @return string the quoted string. * @see DB_common::quoteSmart() * @since Method available since release 1.7.8. */ function quoteFloat($float) { return $this->escapeSimple(str_replace(',', '.', strval(floatval($float)))); } // }}} // {{{ autoCommit() /** * Enables or disables automatic commits * * @param bool $onoff true turns it on, false turns it off * * @return int DB_OK on success. A DB_Error object if the driver * doesn't support auto-committing transactions. */ function autoCommit($onoff = false) { // XXX if $this->transaction_opcount > 0, we should probably // issue a warning here. $this->autocommit = $onoff ? true : false; return DB_OK; } // }}} // {{{ commit() /** * Commits the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function commit() { if ($this->transaction_opcount > 0) { if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $result = @sybase_query('COMMIT', $this->connection); $this->transaction_opcount = 0; if (!$result) { return $this->sybaseRaiseError(); } } return DB_OK; } // }}} // {{{ rollback() /** * Reverts the current transaction * * @return int DB_OK on success. A DB_Error object on failure. */ function rollback() { if ($this->transaction_opcount > 0) { if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $result = @sybase_query('ROLLBACK', $this->connection); $this->transaction_opcount = 0; if (!$result) { return $this->sybaseRaiseError(); } } return DB_OK; } // }}} // {{{ sybaseRaiseError() /** * Produces a DB_Error object regarding the current problem * * @param int $errno if the error is being manually raised pass a * DB_ERROR* constant here. If this isn't passed * the error information gathered from the DBMS. * * @return object the DB_Error object * * @see DB_common::raiseError(), * DB_sybase::errorNative(), DB_sybase::errorCode() */ function sybaseRaiseError($errno = null) { $native = $this->errorNative(); if ($errno === null) { $errno = $this->errorCode($native); } return $this->raiseError($errno, null, null, null, $native); } // }}} // {{{ errorNative() /** * Gets the DBMS' native error message produced by the last query * * @return string the DBMS' error message */ function errorNative() { return @sybase_get_last_message(); } // }}} // {{{ errorCode() /** * Determines PEAR::DB error code from the database's text error message. * * @param string $errormsg error message returned from the database * @return integer an error number from a DB error constant */ function errorCode($errormsg) { static $error_regexps; // PHP 5.2+ prepends the function name to $php_errormsg, so we need // this hack to work around it, per bug #9599. $errormsg = preg_replace('/^sybase[a-z_]+\(\): /', '', $errormsg); if (!isset($error_regexps)) { $error_regexps = array( '/Incorrect syntax near/' => DB_ERROR_SYNTAX, '/^Unclosed quote before the character string [\"\'].*[\"\']\./' => DB_ERROR_SYNTAX, '/Implicit conversion (from datatype|of NUMERIC value)/i' => DB_ERROR_INVALID_NUMBER, '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./' => DB_ERROR_NOSUCHTABLE, '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./' => DB_ERROR_ACCESS_VIOLATION, '/^.+ permission denied on object .+, database .+, owner .+/' => DB_ERROR_ACCESS_VIOLATION, '/^.* permission denied, database .+, owner .+/' => DB_ERROR_ACCESS_VIOLATION, '/[^.*] not found\./' => DB_ERROR_NOSUCHTABLE, '/There is already an object named/' => DB_ERROR_ALREADY_EXISTS, '/Invalid column name/' => DB_ERROR_NOSUCHFIELD, '/does not allow null values/' => DB_ERROR_CONSTRAINT_NOT_NULL, '/Command has been aborted/' => DB_ERROR_CONSTRAINT, '/^Cannot drop the index .* because it doesn\'t exist/i' => DB_ERROR_NOT_FOUND, '/^There is already an index/i' => DB_ERROR_ALREADY_EXISTS, '/^There are fewer columns in the INSERT statement than values specified/i' => DB_ERROR_VALUE_COUNT_ON_ROW, '/Divide by zero/i' => DB_ERROR_DIVZERO, ); } foreach ($error_regexps as $regexp => $code) { if (preg_match($regexp, $errormsg)) { return $code; } } return DB_ERROR; } // }}} // {{{ tableInfo() /** * Returns information about a table or a result set * * NOTE: only supports 'table' and 'flags' if $result * is a table name. * * @param object|string $result DB_result object from a query or a * string containing the name of a table. * While this also accepts a query result * resource identifier, this behavior is * deprecated. * @param int $mode a valid tableInfo mode * * @return array an associative array with the information requested. * A DB_Error object on failure. * * @see DB_common::tableInfo() * @since Method available since Release 1.6.0 */ function tableInfo($result, $mode = null) { if (is_string($result)) { /* * Probably received a table name. * Create a result resource identifier. */ if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) { return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED); } $id = @sybase_query("SELECT * FROM $result WHERE 1=0", $this->connection); $got_string = true; } elseif (isset($result->result)) { /* * Probably received a result object. * Extract the result resource identifier. */ $id = $result->result; $got_string = false; } else { /* * Probably received a result resource identifier. * Copy it. * Deprecated. Here for compatibility only. */ $id = $result; $got_string = false; } if (!is_resource($id)) { return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA); } if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) { $case_func = 'strtolower'; } else { $case_func = 'strval'; } $count = @sybase_num_fields($id); $res = array(); if ($mode) { $res['num_fields'] = $count; } for ($i = 0; $i < $count; $i++) { $f = @sybase_fetch_field($id, $i); // column_source is often blank $res[$i] = array( 'table' => $got_string ? $case_func($result) : $case_func($f->column_source), 'name' => $case_func($f->name), 'type' => $f->type, 'len' => $f->max_length, 'flags' => '', ); if ($res[$i]['table']) { $res[$i]['flags'] = $this->_sybase_field_flags( $res[$i]['table'], $res[$i]['name']); } if ($mode & DB_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & DB_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } // free the result only if we were called on a table if ($got_string) { @sybase_free_result($id); } return $res; } // }}} // {{{ _sybase_field_flags() /** * Get the flags for a field * * Currently supports: * + unique_key (unique index, unique check or primary_key) * + multiple_key (multi-key index) * * @param string $table the table name * @param string $column the field name * * @return string space delimited string of flags. Empty string if none. * * @access private */ function _sybase_field_flags($table, $column) { static $tableName = null; static $flags = array(); if ($table != $tableName) { $flags = array(); $tableName = $table; /* We're running sp_helpindex directly because it doesn't exist in * older versions of ASE -- unfortunately, we can't just use * DB::isError() because the user may be using callback error * handling. */ $res = @sybase_query("sp_helpindex $table", $this->connection); if ($res === false || $res === true) { // Fake a valid response for BC reasons. return ''; } while (($val = sybase_fetch_assoc($res)) !== false) { if (!isset($val['index_keys'])) { /* No useful information returned. Break and be done with * it, which preserves the pre-1.7.9 behaviour. */ break; } $keys = explode(', ', trim($val['index_keys'])); if (sizeof($keys) > 1) { foreach ($keys as $key) { $this->_add_flag($flags[$key], 'multiple_key'); } } if (strpos($val['index_description'], 'unique')) { foreach ($keys as $key) { $this->_add_flag($flags[$key], 'unique_key'); } } } sybase_free_result($res); } if (array_key_exists($column, $flags)) { return(implode(' ', $flags[$column])); } return ''; } // }}} // {{{ _add_flag() /** * Adds a string to the flags array if the flag is not yet in there * - if there is no flag present the array is created * * @param array $array reference of flags array to add a value to * @param mixed $value value to add to the flag array * * @return void * * @access private */ function _add_flag(&$array, $value) { if (!is_array($array)) { $array = array($value); } elseif (!in_array($value, $array)) { array_push($array, $value); } } // }}} // {{{ getSpecialQuery() /** * Obtains the query string needed for listing a given type of objects * * @param string $type the kind of objects you want to retrieve * * @return string the SQL query string or null if the driver doesn't * support the object type requested * * @access protected * @see DB_common::getListOf() */ function getSpecialQuery($type) { switch ($type) { case 'tables': return "SELECT name FROM sysobjects WHERE type = 'U'" . ' ORDER BY name'; case 'views': return "SELECT name FROM sysobjects WHERE type = 'V'"; default: return null; } } // }}} } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?> DB-1.7.14/doc/IDEAS0000660000175100017510000000653711626162707013211 0ustar danielcdanielcAbstracted Types (Stig) ----------------------- DB needs a set of types representing the most commonly used types in all backends. This type set could also be geared towards integration with things like XML-RPC/SOAP implementations, HTML form classes, etc. Real Query Parser (Stig) ------------------------ With a real query parser, DB can implement more of its portability based on the query, instead of having support functions for everything. One example would be LIMIT, another "INSERT ... RETURNING". Portable transactions (Stig) ---------------------------- If DB can parse queries enough to determine what tables are affected by queries, it should be possible to make a replayable transaction log. GNOME uses an XML format for configuration data that lets you checkpoint state once in a while, and revert to that state later. With a similar approach for transactions in DB we can implement portable transactions and checkpointing even for the databases that don't support them. Error reporting clean-up/debug (Tomas) ------------------------------------- Now each driver has its own raiseError method, common has a raiseError and DB has a DB_error class and its own isError() method. This error stuff overhead could be simplified with only one raiseError, droping the DB Error class and also the DB::isError() (use the PEAR.php ones instead). Other idea could be to add a system for allowing people access to all the queries sended by PEAR DB to the backend. Also a new PEAR_ERROR_DEBUG flag that automatically (show|triggers) debug info, perhaps with a new PEAR_(Warning|Debug) object. Quote clean-up (Stig) --------------------- 1. Keep quote and quoteString, but move quoting of strings back into quoteString and make quote call it for strings. 2. Add an optional "operator" parameter to quote that is one of "=", "<", ">" or "<>" that will be inserted in front of the quoted value unless it is NULL, in which case it will be converted to "IS" (for "=") or "IS NOT" (for the others). Auto free statements (Tomas) ---------------------------- By setting a param in query() or for the hole DB instance, PEAR DB could auto-free results in DB_result->fetch(Into|Row) when the driver returns false. Datatypes in prepare syntax (Tomas) ----------------------------------- Extend the actual prepare/execute placeholders to support data types, both to check the data introduced to the query and to "cast" the result to native php data types. Ex: $sql = "INSERT INTO table VALUES ({{int(4)}}, {{bool}}, {{date('Y-m-d')}})"; $row = $db->query($sql, array(8, 't', '2001-04-1')); Format: {{(,)}} "param" could be the max lenght of the data, date formats, not_null checks or default values. Other ideas could be: 1) $sql = "INSERT INTO table VALUES (?, ?, ?)"; $sth = $db->prepare($sql, array('int(4)', 'bool', 'date'); $res = $db->execute($sth, array($a, $b, $c); 2) $sql = "INSERT INTO table VALUES (?, ?, ?)"; $params = array( 0 => array($a, 'int(4)'), 1 => array($b, 'bool') ); $res = $db->query($sql, $params); Auto connect feature (Tomas) ---------------------------- Add the ability to create for example a light and dump DB object which will only set up the connection when needed. With that people could create the DB object in a common prepend or default file without the need to waste system resources if the use of the database is finally not needed. DB-1.7.14/doc/MAINTAINERS0000660000175100017510000000125011626162707014121 0ustar danielcdanielcMaintainers for DB database backends/drivers: dbase : Daniel Convissor fbsql : Daniel Convissor Frank M. Kromann ibase : Daniel Convissor ifx : Daniel Convissor msql : Daniel Convissor mssql : Daniel Convissor mysql : Daniel Convissor mysqli : Daniel Convissor oci8 : Daniel Convissor odbc : Daniel Convissor pgsql : Daniel Convissor sqlite : Daniel Convissor sybase : Daniel Convissor DB-1.7.14/doc/STATUS0000660000175100017510000001044611626162707013401 0ustar danielcdanielcSTATUS OF THE PEAR DB PACKAGE ============================= DB Driver Feature Matrix ------------------------ Symbols: x = implemented, but without tests t = implemented, but one or more tests fail T = implemented, passing all tests e = emulated, without tests l = emulated, but one or more tests fail E = emulated, passing all tests n = returns "not capable" - = no implementation of this feature or status unknown fbsql ifx mssql mysqli odbc sqlite FEATURE dbase | ibase | msql | mysql | oci8 | pgsql | sybase simpleQuery - T T T T T T T T T T T T numCols x T T T T T T T T T T T T numRows x T E E T T T T E T T T T errorNative n T T T T T T T T T T E T prepare/execute e E T E E E E E T E E E E sequences n T T n T T T T T E T E T affectedRows n T E E T T T T T T E T T fetch modes x T T T T T T T T T T T T fetch row by no x x n n x x x x n x x x x transactions - T T T n T T T T T T n T auto-commit n T E E n E E E E T E n E error mapping - T T T T T T T T T T T T tableInfo x T T t T T T T T T T T T getListOf() TYPES tables - T T T T T T T T T T T T views - T T - - T - - - T T - T users - T T - - - T T - - T - - databases - - - - T - T T - T T - - functions - T - - - - - - - - T - - synonyms - - - - - - - - T - - - - Test Conformance ---------------- Symbols: o = Test passed X = Test failed L = Some portions of the test failed due to limitations in PHP or DBMS n = Test returns "not capable" - = Not tested fbsql ifx mssql mysqli odbc sqlite dbase | ibase | msql | mysql | oci8 | pgsql | sybase 01connect o o o o o o o o o o o o o 02fetch - o o o o o o o o o o o o 03simplequery - o o o o o o o o o o o o 04numcols - o o o o o o o o o o o o 05sequences - o o o o o o o o o o o o 06prepexec - o o o L o o o o o o o o 08affectedrows - o o o o o o o o o o o o 09numrows - o o o o o o o o o o o o 10errormap - o o o o o o o o o o o o 11transactions - o o o n o o o o o o n o 13limit - o o o o o o o o o o o o 14fetchmode_obje - o o o o o o o o o o o o 15quote - o o o o o o o o o o o o 16tableinfo - o o L o o o o o o o o o 17query - o o o o o o o o o o o o 18get - o o o o o o o o o o o o 19getlistof - o o o o o o o o o o o o DBMS Versions Tested -------------------- dbase n/a fbsql 4.1.6 ibase Firebird 1.5.1 (PHP 5 only) ifx 7.2 Standard Edtition msql 3.6 (PHP snapshots dated 2005-02-18) mssql 8.0.760 mysql 4.0.21 mysqli 4.1.5 (PHP 5 only) oci8 9.2 odbc DB2 ESE 8.1 and MS Access 2000 pgsql 7.4.1 and 8.0.1 sqlite PHP 5: extension. PHP 4: PECL snapshot. sybase ASE 12.5.3 Tests were performed under both of the following PHP versions unles otherwise noted: 4.3.11-dev dated 2005-02-22 5.1.0-dev dated 2005-02-22 DB-1.7.14/doc/TESTERS0000660000175100017510000001165511626162707013512 0ustar danielcdanielc=================== HOW TO TEST PEAR DB =================== INTRODUCTION ============ These are instructions for testing PEAR DB on a Windows machine using a Cygwin Bash shell. Adjust the paths and commands to match your system. This configuration is used because these precise steps are known to work. NOTE: You must log on as a user which has permissions to modify the contents of your PHP executable's directory. This is necessary for both configuring AND running the test system. INSTALLATION ============ Obtain PHP's Test Framework --------------------------- If you don't have PHP's test framework, you need to obtain it. These steps include changing the working directory, downloading run-tests.php via SVN and copying the file into place. Change the branch in the SVN command as appropriate for your present version of PHP. cd c:/progra~1/php svn checkout \ https://svn.php.net/repository/php/php-src/branches/PHP_5_3/run-tests.php Obtain DB and its Test Framework -------------------------------- * IF PEAR DB IS ALREADY INSTALLED: If you have PEAR DB installed already, good. The test suite is in place. Open up a command/shell prompt and move into the test directory. cd /tests/DB/tests * VIA A NEW INSTALLATION USING THE PEAR INSTALLER: Installing PEAR has gotten fairly easy. Follow the instructions from the manual: http://pear.php.net/manual/en/installation.php Once PEAR and DB are installed, move to the test directory. cd pear/tests/DB/tests * VIA SVN: Create a location to store the test installation of DB and its test scripts. d: mkdir peartest svn checkout https://svn.php.net/repository/pear/packages/DB/trunk peartest cd peartest We assume you already have the PEAR base package installed. If you don't, you will need to do so, but the instructions for doing that are beyond the scope of this document. See http://pear.php.net/manual/en/installation.php for more info. Move to the test directory. cd pear/DB/tests Copy the Starter Shell Script and Edit the Paths ------------------------------------------------ To make starting up each test run easier, we have included two shell scripts. The original files are named "run.cvs". They need to be renamed to "run" so SVN won't bother you with tracking them. Then, the paths and file names in them need to be set to those used by your system. cp run.cvs run chmod 755 run vi run cd driver cp run.cvs run chmod 755 run vi run Copy the Setup File and Edit the DSN's -------------------------------------- The test suite contains a file in the driver directory that stores the DSN's needed to connect to your database. Then you'll need to edit the DSN's in it. vi setup.inc RUN THE TESTS ============= To run all tests: ./run To run one test: ./run Example: ./run db_parsedsn.phpt Test Types and Locations ------------------------ tests Common PEAR DB tests tests/driver Common tests for all the drivers Results and What To Do With Them -------------------------------- Each test that fails generates a .php (which you can execute), a .exp (the expected output), a .out (the test output) and a .diff (a diff -u from the .exp and .out files). If you run the tests, please report or fill the TEST CONFORMANCE table in the STATUS document. Before any commit to SVN be sure to run the tests and nothing got broken with the change. If you get the message "SKIP", means that the test it's not executed. Look at the DB/tests/driver/skipif.inc to see what's the problem (probably a connection problem). DB TESTER MATRIX ================ fbsql ifx mssql mysqli odbc sqlite TESTER dbase | ibase | msql | mysql | oci8 | pgsql | sybase John Horton - - - X - - - - - - - - - Tim Zickus - - - - - - - - X - - - - Tim Parkin - - - - - - - - X - - - - Paul Gardiner - - - X - - - - - - - - - peterwb@iafrica.com - - - X - - - - - - - - - Daniel, Adam - - - - - - - - X - - - - szii@sziisoft.com - - - - - - - - - X¹ - - - jmh3@linuxfreak.com - - - - - - - - - - X - - Kevin Henrikson - - - - - - - - X - - - - Stig Bakken - - - - - - X - - - X - - Chuck Hagenbuch - - - - - X - - - - - - - Ludovico Magnocavallo - - X - - - - - - - - - - Daniel Convissor X X X - X X X X X X² X X X MISSING TESTERS - - - - - - - - - - - - - Comments: [1]: ODBC using IBM DB2 [2]: ODBC using IBM DB2 and MS Access DB-1.7.14/tests/driver/01connect.phpt0000660000175100017510000000376111626162707017014 0ustar danielcdanielc--TEST-- DB_driver::connect --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- toString()); } if (is_object($dbh)) { print "$name is an object\n"; } switch ($dbh->phptype) { case 'dbase': if (is_int($dbh->connection)) { print "$name is connected\n"; } else { print "$name NOT connected\n"; } break; case 'mysqli': if (is_a($dbh->connection, 'mysqli')) { print "$name is connected\n"; } else { print "$name NOT connected\n"; } break; case 'sybase': if (gettype($dbh->connection) == 'resource' || (gettype($dbh->connection) == 'integer' && $dbh->connection > 0)) { print "$name is connected\n"; } else { print "$name NOT connected\n"; } break; default: if (gettype($dbh->connection) == 'resource') { print "$name is connected\n"; } else { print "$name NOT connected\n"; } } } check_dbh($dbh, '$dbh'); $test_array_dsn = DB::parseDSN($dsn); foreach ($test_array_dsn as $key => $value) { if ($value === false) { unset($test_array_dsn[$key]); } } $dbha =& DB::connect($test_array_dsn, $options); check_dbh($dbha, '$dbha'); $tmp = serialize($dbha); $dbhu = unserialize($tmp); check_dbh($dbhu, '$dbhu'); ?> --EXPECT-- $dbh is an object $dbh is connected $dbha is an object $dbha is connected $dbhu is an object $dbhu is connected DB-1.7.14/tests/driver/02fetch.phpt0000660000175100017510000000232411626162707016447 0ustar danielcdanielc--TEST-- DB_driver::fetch --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- --EXPECT-- testing fetchrow: row 1: 42, bing, This is a test, 1999-11-21 row 2: 1, one, One, 2001-02-16 row 3: 2, two, Two, 2001-02-15 row 4: 3, three, Three, 2001-02-14 row 5: NULL testing fetchmodes: fetchrow default default, portability mode DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM 0 1 2 3 output matched expected format testing fetchmodes: fetchinto default default 0 1 2 3 42 bing This is a test 1999-11-21 testing fetchmodes: fetchrow ordered default 0 1 2 3 testing fetchmodes: fetchrow assoc default a b cc d testing fetchmodes: fetchrow ordered default with assoc specified a b cc d testing fetchmodes: fetchrow assoc default with ordered specified 0 1 2 3 testing fetchmodes: fetchinto ordered default 0 1 2 3 testing fetchmodes: fetchinto assoc default a b cc d testing fetchmodes: fetchinto ordered default with assoc specified a b cc d testing fetchmodes: fetchinto assoc default with ordered specified 0 1 2 3 testing fetchmodes: fetchrow assoc quoted identifiers a b cc d DB-1.7.14/tests/driver/03simplequery.phpt0000660000175100017510000000042011626162707017731 0ustar danielcdanielc--TEST-- DB_driver::simpleQuery --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- --EXPECT-- passed DB-1.7.14/tests/driver/04numcols.phpt0000660000175100017510000000041111626162707017033 0ustar danielcdanielc--TEST-- DB_driver::numCols --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- --EXPECT-- 1 2 3 4 DB-1.7.14/tests/driver/05sequences.phpt0000660000175100017510000000111311626162707017347 0ustar danielcdanielc--TEST-- DB_driver::sequences --INI-- error_reporting = 2047 --SKIPIF-- dropSequence('ajkdslfajoijkadie'); if (DB::isError($tableInfo) && $tableInfo->code == DB_ERROR_NOT_CAPABLE) { die("skip $tableInfo->message"); } ?> --FILE-- --EXPECT-- an error is the proper response here an error cought by the error handler is good a=1 b=2 b-a=1 c=1 d=1 e=1 DB-1.7.14/tests/driver/06prepexec.phpt0000660000175100017510000000243611626162707017201 0ustar danielcdanielc--TEST-- DB_driver::prepare/execute --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- --EXPECT-- ------------1------------ sth1,sth2,sth3,sth4 created sth1: ? as param, passing as array... sth1 executed sth2: ! and ? as params, passing as array... sth2 executed sth3: ?, ! and & as params, passing as array... sth3 executed sth4: no params... sth4 executed results: |72 - a - - | |72 - direct - - | |72 - it's good - opaque placeholder's test - | |72 - that's right - - | ------------2------------ results: |72 - set1 - opaque placeholder's test - 1234-56-78| |72 - set2 - opaque placeholder's test - | |72 - set3 - opaque placeholder's test - | ------------3------------ TRUE FALSE ------------4------------ |72 - set1 - opaque placeholder's test - 1234-56-78| |72 - set2 - opaque placeholder's test - | |72 - set3 - opaque placeholder's test - | ~~ ~~ |72 - set1 - opaque placeholder's test - 1234-56-78| ~~ |72 - set1 - opaque placeholder's test - 1234-56-78| |72 - set2 - opaque placeholder's test - | |72 - set3 - opaque placeholder's test - | ~~ ------------5------------ insert: okay a = 11, b = three, d = got expected outcome DB-1.7.14/tests/driver/08affectedrows.phpt0000660000175100017510000000333211626162707020040 0ustar danielcdanielc--TEST-- DB_driver::affectedRows --INI-- error_reporting = 2047 --SKIPIF-- phptype == 'ibase' && version_compare(PHP_VERSION, '5.0.0', '<')) { die('skip ibase doesn\'t return affected rows from ibase_query in PHP 4'); } ?> --FILE-- setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); die($o->toString()); } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); // Clean table $dbh->query("DELETE FROM phptest"); // Affected rows by INSERT statement $dbh->query("INSERT INTO phptest (a,b) VALUES(1, 'test')"); $dbh->query("INSERT INTO phptest (a,b) VALUES(2, 'test')"); printf("%d after insert\n", $dbh->affectedRows()); // Affected rows by SELECT statement $dbh->query("SELECT * FROM phptest"); printf("%d after select\n", $dbh->affectedRows()); $dbh->query("DELETE FROM phptest WHERE b = 'test'"); printf("%d after delete\n", $dbh->affectedRows()); // Affected rows by DELETE statement $dbh->query("INSERT INTO phptest (a,b) VALUES(1, 'test')"); $dbh->query("INSERT INTO phptest (a,b) VALUES(2, 'test')"); $dbh->query("DELETE FROM phptest"); printf("%d after delete all\n", $dbh->affectedRows()); $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); ?> --EXPECT-- 1 after insert 0 after select 2 after delete 2 after delete all DB-1.7.14/tests/driver/09numrows.phpt0000660000175100017510000000105311626162707017075 0ustar danielcdanielc--TEST-- DB_driver::numRows --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- --EXPECT-- (want 1) got 1 from first (want 2) got 2 from 0 (want 3) got 3 from 1 (want 4) got 4 from 2 (want 5) got 5 from 3 (want 6) got 6 from 4 (want 5) got 5 from > 0 (passing params to query) (want 4) got 4 from < 4 (doing prepare/execute) (want 2) got 2 from 5 and 6 not deleted (want 0) got 0 from < 0 DB-1.7.14/tests/driver/10errormap.phpt0000660000175100017510000000367411626162707017215 0ustar danielcdanielc--TEST-- DB_driver::error mapping --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- --EXPECT-- DB_ERROR_NOSUCHTABLE for select: matches expected outcome DB_ERROR_NOSUCHTABLE for drop: matches expected outcome DB_ERROR_NOT_FOUND for drop index: matches expected outcome DB_ERROR_ALREADY_EXISTS for create table: matches expected outcome DB_ERROR_ALREADY_EXISTS for create index: matches expected outcome DB_ERROR_CONSTRAINT for primary key insert duplicate: matches expected outcome DB_ERROR_CONSTRAINT for primary key update duplicate: matches expected outcome DB_ERROR_CONSTRAINT for unique key insert duplicate: matches expected outcome DB_ERROR_CONSTRAINT for unique key update duplicate: matches expected outcome DB_ERROR_CONSTRAINT for foreign key on insert: matches expected outcome DB_ERROR_CONSTRAINT for foreign key on delete: matches expected outcome DB_ERROR_CONSTRAINT_NOT_NULL on insert: matches expected outcome DB_ERROR_CONSTRAINT_NOT_NULL on update: matches expected outcome DB_ERROR_NOSUCHFIELD joining ON bogus column: matches expected outcome DB_ERROR_NOSUCHFIELD joining USING bogus column: matches expected outcome DB_ERROR_DIVZERO: matches expected outcome DB_ERROR_INVALID_NUMBER putting chars in INT column: matches expected outcome DB_ERROR_INVALID_NUMBER putting float in INT column: matches expected outcome DB_ERROR_INVALID_NUMBER putting excessive int in INT column: matches expected outcome DB_ERROR_INVALID_NUMBER putting int in CHAR column: matches expected outcome DB_ERROR_NOSUCHFIELD: matches expected outcome DB_ERROR_SYNTAX: matches expected outcome DB_ERROR_VALUE_COUNT_ON_ROW: matches expected outcome DB_ERROR_INVALID on CHAR column data too long: matches expected outcome DB_ERROR_INVALID on VARCHAR column data too long: matches expected outcome DB-1.7.14/tests/driver/11transactions.phpt0000770000175100017510000000145711626162707020076 0ustar danielcdanielc--TEST-- DB_driver::transaction test --INI-- error_reporting = 2047 --SKIPIF-- features['transactions']) { die('skip this driver does not support transactions'); } ?> --FILE-- --EXPECT-- 1) after autocommit: bing one. ops=ok 2) before commit: bing one two three. ops=ok 3) after commit: bing one two three. ops=ok 4) before rollback: bing one two three four five. ops=ok 5) after rollback: bing one two three. ops=ok 6) before autocommit+rollback: bing one two three six seven. ops=ok 7) after autocommit+rollback: bing one two three six seven. ops=ok 8) testing that select doesn't disturbe opcount: ok DB-1.7.14/tests/driver/13limit.phpt0000660000175100017510000000252111626162707016475 0ustar danielcdanielc--TEST-- DB_driver::row limit --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- --EXPECT-- ======= From: 0 || Number of rows to fetch: 10 ======= 1.- result 0 2.- result 1 3.- result 2 4.- result 3 5.- result 4 6.- result 5 7.- result 6 8.- result 7 9.- result 8 10.- result 9 Row count for limited result: 10 ======= From: 10 || Number of rows to fetch: 10 ======= 11.- result 10 12.- result 11 13.- result 12 14.- result 13 15.- result 14 16.- result 15 17.- result 16 18.- result 17 19.- result 18 20.- result 19 Row count for limited result: 10 ======= From: 20 || Number of rows to fetch: 10 ======= 21.- result 20 22.- result 21 23.- result 22 24.- result 23 25.- result 24 26.- result 25 27.- result 26 28.- result 27 29.- result 28 30.- result 29 Row count for limited result: 10 ======= From: 30 || Number of rows to fetch: 10 ======= 31.- result 30 32.- result 31 33.- result 32 Row count for limited result: 3 ======= Passing $params || From: 11 || Number of rows to fetch: 3 ======= 12.- result 11 13.- result 12 14.- result 13 ======= From: 0 || Number of rows to fetch: 3 || Iterations: 10 ======= Row count for limited result: 3 DB-1.7.14/tests/driver/14fetchmode_object.phpt0000660000175100017510000000127011626162707020644 0ustar danielcdanielc--TEST-- DB_driver::fetchmode object --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- --EXPECT-- --- fetch with param DB_FETCHMODE_OBJECT --- stdclass -> a b cc d stdclass -> a b cc d --- fetch with default fetchmode DB_FETCHMODE_OBJECT --- stdclass -> a b cc d stdclass -> a b cc d --- fetch with default fetchmode DB_FETCHMODE_OBJECT and class DB_row --- db_row -> a b cc d db_row -> a b cc d --- fetch with default fetchmode DB_FETCHMODE_OBJECT with no class then DB_row --- stdclass -> a b cc d db_row -> a b cc d DB-1.7.14/tests/driver/15quote.phpt0000660000175100017510000001364011626162707016522 0ustar danielcdanielc--TEST-- DB_driver::quote --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'pearquote'); die($o->toString()); } // DBMS boolean column type simulation... $boolean_col_type = array( 'dbase' => 'Logical', 'fbsql' => 'BOOLEAN', 'ibase' => 'SMALLINT', 'ifx' => 'SMALLINT', 'msql' => 'INTEGER', 'mssql' => 'BIT', 'mysql' => 'TINYINT(1)', 'mysqli' => 'TINYINT(1)', 'oci8' => 'NUMBER(1)', 'odbc' => 'SMALLINT', 'pgsql' => 'BOOLEAN', 'sqlite' => 'INTEGER', 'sybase' => 'TINYINT', ); // adjust things for specific DBMS's if ($dbh->phptype == 'odbc') { if ($dbh->dbsyntax == 'odbc') { $type = $dbh->phptype; } else { $type = $dbh->dbsyntax; } } else { $type = $dbh->phptype; } switch ($type) { case 'access': $decimal = 'SINGLE'; $null = ''; $chr = 'VARCHAR(8)'; $identifier = 'q\ut "dnt'; break; case 'db2': case 'ibase': $decimal = 'DECIMAL(3,1)'; $null = ''; $chr = 'VARCHAR(8)'; $identifier = 'q\ut] "dn[t'; break; case 'ifx': // doing this for ifx to keep certain versions happy $decimal = 'DECIMAL(3,1)'; $null = ''; $chr = 'CHAR(8)'; $identifier = ''; break; case 'msql': $decimal = 'REAL'; $null = ''; $chr = 'CHAR(8)'; $identifier = ''; break; case 'fbsql': case 'oci8': $decimal = 'DECIMAL(3,1)'; $null = ''; $chr = 'VARCHAR(8)'; $identifier = 'q\ut] dn[t'; break; default: $decimal = 'DECIMAL(3,1)'; $null = 'NULL'; $chr = 'VARCHAR(8)'; $identifier = 'q\ut] "dn[t'; } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'pearquote'); if ($identifier) { switch ($dbh->phptype) { case 'sybase': $res = $dbh->query('set quoted_identifier on'); if (DB::isError($res) ) { pe($res); } break; default: } $create = $dbh->query(" CREATE TABLE pearquote ( n $decimal $null, s $chr $null, " . $dbh->quoteIdentifier($identifier) . " $decimal $null, b {$boolean_col_type[$dbh->phptype]} $null ) "); if (DB::isError($create) ) { pe($create); } $info = $dbh->tableInfo('pearquote'); if (DB::isError($info) ) { if ($info->code == DB_ERROR_NOT_CAPABLE) { print "Got outcome expected from delimited identifier.\n"; } else { print "tableInfo() failed.\n"; } } else { if ($identifier == $info[2]['name']) { print "Got outcome expected from delimited identifier.\n"; // print "COLUMN NAME IS: {$info[2]['name']}\n"; } else { print "Expected column name: '$identifier' ... "; print "Actual column name: '{$info[2]['name']}'\n"; } } } else { $dbh->query(" CREATE TABLE pearquote ( n $decimal $null, s $chr $null, plainidentifier $decimal $null, b {$boolean_col_type[$dbh->phptype]} $null ) "); print "Got outcome expected from delimited identifier.\n"; } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $strings = array( "'", "\"", "\\", "%", "_", "''", "\"\"", "\\\\", "\\'\\'", "\\\"\\\"" ); $nums = array( 12.3, 15, ); $bools = array( TRUE, FALSE, ); echo "String escape test: "; foreach ($strings as $s) { $quoted = $dbh->quoteSmart($s); $dbh->query("INSERT INTO pearquote (s) VALUES ($quoted)"); } $diff = array_diff($strings, $res = $dbh->getCol("SELECT s FROM pearquote")); if (count($diff) > 0) { echo "FAIL"; print_r($strings); print_r($res); } else { echo "OK"; } $dbh->query("DELETE FROM pearquote"); echo "\nNumber escape test: "; foreach ($nums as $n) { $quoted = $dbh->quoteSmart($n); $dbh->query("INSERT INTO pearquote (n) VALUES ($quoted)"); } $diff = array(); $res =& $dbh->getCol('SELECT n FROM pearquote ORDER BY n'); foreach ($nums as $key => $val) { if ($val != $res[$key]) { $diff[] = "$val != {$res[$key]}"; } } if (count($diff) > 0) { echo "FAIL\n"; print_r($diff); } else { echo 'OK'; } $dbh->query('DELETE FROM pearquote'); echo "\nBoolean escape test: "; $i = 1; foreach ($bools as $b) { $quoted = $dbh->quoteSmart($b); $dbh->query("INSERT INTO pearquote (n, b) VALUES ($i, $quoted)"); $i++; } $diff = array(); $res =& $dbh->getCol('SELECT b, n FROM pearquote ORDER BY n'); foreach ($bools as $key => $val) { if ($val === true) { if ($res[$key] == 1 || $res[$key] == true || substr(strtolower($res[$key]), 0, 1) == 't') { // good } else { $diff[] = "in:true != out:{$res[$key]}"; } } else { if ($res[$key] == 0 || $res[$key] == false || substr(strtolower($res[$key]), 0, 1) == 'f') { // good } else { $diff[] = "in:false != out:{$res[$key]}"; } } } if (count($diff) > 0) { echo "FAIL\n"; print_r($diff); } else { echo "OK\n"; } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'pearquote'); ?> --EXPECT-- Got outcome expected from delimited identifier. String escape test: OK Number escape test: OK Boolean escape test: OK DB-1.7.14/tests/driver/16tableinfo.phpt0000770000175100017510000011000711626162707017326 0ustar danielcdanielc--TEST-- DB_driver::tableInfo --INI-- error_reporting = 2047 --SKIPIF-- $quirks array, which has the following format: * *
 * 'driver' => array(
 *     'clob' => DBMS's column type for creating CLOB fields
 *     'date' => DBMS's column type for creating DATE fields
 *     'dateliteral' => The SQL keyword necessary for defining dates
 *     'finds_table' => Can this DBMS determine table names from queries?
 *     'size_from_table' => Does this DBMS know the column size via table name?
 *     'handles_results' => Can the DBMS get info from query results?
 *     'commands' => array(
 *         Extra commands to be passed to PHP's eval() function
 *     )
 *     0 => array(
 *         //  Info expected to be reported for phptest_fk.a
 *         //  (INTEGER NOT NULL) (UNIQUE KEY with d)
 *         'type' => Column type reported by the DBMS
 *         'len' => Column size reported by the DBMS
 *         'flags' => Flags reported by the DBMS
 *     )
 *     1 => array()  Info expected to be reported for phptest_fk.fk
 *                   (INTEGER NOT NULL) (PRIMARY KEY)
 *     2 => array()  Info expected to be reported for phptest_fk.c
 *                   (CLOB/CHAR/VARCHAR NULL)
 *     3 => array()  Info expected to be reported for phptest_fk.d
 *                   (DATE NOT NULL) (UNIQUE KEY with a)
 *     4 => array()  Info expected to be reported for phptest_fk.e
 *                   (CHAR(2) DEFAULT ' e' NOT NULL)
 *     5 => array()  Info expected to be reported for phptest_fk.f
 *                   (DECIMAL(2,1) NULL)
 *     9 => array()  Info expected to be reported for phptest.d
 *                   (VARCHAR(20) NULL)
 * )
 * 
* * @category Database * @package DB * @version $Id: 16tableinfo.phpt 306605 2010-12-24 06:22:59Z aharvey $ * @author Daniel Convissor * @see DB_common::tableInfo() */ error_reporting(E_ALL); chdir(dirname(__FILE__)); require_once dirname(__FILE__) . '/skipif.inc'; $tableInfo = $dbh->tableInfo('ajkdslfajoijkadie'); if (DB::isError($tableInfo) && $tableInfo->code == DB_ERROR_NOT_CAPABLE) { die("skip $tableInfo->message"); } ?> --FILE-- getMessage() == "DB Error: can't distinguish duplicate field names") { print "NOTICE: $dbh->phptype can't distinguish duplicate field names"; return; } if ($o->getCode() == DB_ERROR_NOT_CAPABLE && !$quirks[$dbh->phptype . ':' . $dbh->dbsyntax]['handles_results']) { return; } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); drop_table($dbh, 'phptest_fk'); die($o->toString()); } /** * Loop through an array returned from tableInfo(), compare the actual * contents to the expected contents. If the actual results match the * expectations, say so. If not, say so and show the information. * * @param array $array the array to be examined * @param string $expected the expected contents of the array * @param string $field field index number of $quriks and table * @param boolean $query true if array is from a query or false if array * is tableInfo() * @return void */ function examineArrayData($array, $expected, $field = false, $query = true) { global $dbh, $quirks; $quirk_key = $dbh->phptype . ':' . $dbh->dbsyntax; if (DB::isError($array) && $array->getCode() == DB_ERROR_NOT_CAPABLE) { print "matched expected result\n"; return; } if (!is_array($array)) { print "This DMBS didn't produce proper results\n"; return; } if (is_int($field)) { $array = $array[$field]; } $actual = ''; foreach ($array as $key => $value) { if ($field !== false && isset($quirks[$quirk_key][$field][$key])) { if ($key == 'flags' && $value == '' && $query && !$quirks[$quirk_key]['finds_table']) { $actual .= "$key ... matched expected value\n"; } else { if ($quirks[$quirk_key][$field][$key] == $value) { $actual .= "$key ... matched expected value\n"; } else { if ($value == 0 && !$quirks[$quirk_key]['size_from_table']) { $actual .= "$key ... matched expected value\n"; } else { $actual .= "$key ... was '$value' but we expected "; $actual .= "'{$quirks[$quirk_key][$field][$key]}'\n"; } } } } else { if ($key == 'table') { if ($field <= 5) { if ($value == 'phptest_fk') { $actual .= "$key ... matched expected value\n"; } else { if ($value == '' && $query && !$quirks[$quirk_key]['finds_table']) { $actual .= "$key ... matched expected value\n"; } else { $actual .= "$key ... was '$value' but we expected 'phptest_fk'\n"; } } } else { if ($value == 'phptest') { $actual .= "$key ... matched expected value\n"; } else { if ($value == '' && $query && !$quirks[$quirk_key]['finds_table']) { $actual .= "$key ... matched expected value\n"; } else { $actual .= "$key ... was '$value' but we expected 'phptest_fk'\n"; } } } } else { $actual .= "$key => $value\n"; } } } if ($actual == $expected) { print "matched expected result\n"; } else { print "DIDN'T match expected values...\n"; print "~~~~~~~~\nExpected:\n$expected\n"; print "~~~~\nActual:\n$actual\n~~~~~~~~\n\n"; } } /** * Loop through an array of table info data and return the results. * * @param array $array the array to be examined * @return string */ function returnArrayData($array) { global $dbh, $quirks; $quirk_key = $dbh->phptype . ':' . $dbh->dbsyntax; if (!$quirks[$quirk_key]['handles_results']) { return "\n"; } $out = ''; foreach ($array as $key => $value) { $out .= "$key => $value\n"; } return $out; } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $quirks = array( 'fbsql:fbsql' => array( 'clob' => 'CHAR(29)', 'date' => 'DATE', 'dateliteral' => ' DATE ', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( ), 0 => array( 'type' => 'INTEGER', 'len' => 0, 'flags' => '', ), 1 => array( 'type' => 'INTEGER', 'len' => 0, 'flags' => 'not_null', ), 2 => array( 'type' => 'CHARACTER', 'len' => 29, 'flags' => '', ), 3 => array( 'type' => 'DATE', 'len' => 0, 'flags' => '', ), 4 => array( 'type' => 'CHARACTER', 'len' => 2, 'flags' => '', ), 5 => array( 'type' => 'DECIMAL', 'len' => 0, 'flags' => '', ), 9 => array( 'type' => 'CHARACTER', 'len' => 20, 'flags' => '', ), ), 'ibase:ibase' => array( 'clob' => 'VARCHAR(50)', 'date' => 'DATE', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( ), 0 => array( 'type' => 'INTEGER', 'len' => 4, 'flags' => 'unique_key not_null', ), 1 => array( 'type' => 'INTEGER', 'len' => 4, 'flags' => 'primary_key not_null', ), 2 => array( 'type' => 'VARCHAR', 'len' => 50, 'flags' => '', ), 3 => array( 'type' => 'DATE', 'len' => 4, 'flags' => 'unique_key not_null', ), 4 => array( 'type' => 'CHAR', 'len' => 2, 'flags' => 'not_null default', ), 5 => array( 'type' => 'NUMERIC(9,1)', 'len' => 4, 'flags' => '', ), 9 => array( 'type' => 'VARCHAR', 'len' => 20, 'flags' => '', ), ), 'ibase:firebird' => array( 'clob' => 'VARCHAR(50)', 'date' => 'DATE', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( ), 0 => array( 'type' => 'INTEGER', 'len' => 4, 'flags' => 'unique_key not_null', ), 1 => array( 'type' => 'INTEGER', 'len' => 4, 'flags' => 'primary_key not_null', ), 2 => array( 'type' => 'VARCHAR', 'len' => 50, 'flags' => '', ), 3 => array( 'type' => 'DATE', 'len' => 4, 'flags' => 'unique_key not_null', ), 4 => array( 'type' => 'CHAR', 'len' => 2, 'flags' => 'not_null default', ), 5 => array( 'type' => 'NUMERIC(9,1)', 'len' => 4, 'flags' => '', ), 9 => array( 'type' => 'VARCHAR', 'len' => 20, 'flags' => '', ), ), 'ifx:ifx' => array( 'clob' => 'CHAR(29)', 'date' => 'CHAR(10)', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( ), 0 => array( 'type' => 'SQLINT', 'len' => 4, 'flags' => 'not_null', ), 1 => array( 'type' => 'SQLINT', 'len' => 4, 'flags' => 'not_null', ), 2 => array( 'type' => 'SQLCHAR', 'len' => 29, 'flags' => '', ), 3 => array( 'type' => 'SQLCHAR', 'len' => 10, 'flags' => 'not_null', ), 4 => array( 'type' => 'SQLCHAR', 'len' => 2, 'flags' => 'not_null', ), 5 => array( 'type' => 'SQLDECIMAL', 'len' => 513, 'flags' => '', ), 9 => array( 'type' => 'SQLCHAR', 'len' => 20, 'flags' => '', ), ), 'msql:msql' => array( 'clob' => 'TEXT(255)', 'date' => 'CHAR(10)', 'dateliteral' => '', 'finds_table' => true, 'size_from_table' => true, 'handles_results' => true, 'commands' => array( ), 0 => array( 'type' => 'int', 'len' => 4, 'flags' => 'not_null', ), 1 => array( 'type' => 'int', 'len' => 4, // for some reason, the unique property always contains 0 // 'flags' => 'unique_key not_null', 'flags' => 'not_null', ), 2 => array( 'type' => 'text', 'len' => 255, 'flags' => '', ), 3 => array( 'type' => 'char', 'len' => 10, 'flags' => 'not_null', ), 4 => array( 'type' => 'char', 'len' => 2, 'flags' => 'not_null', ), 5 => array( 'type' => 'real', 'len' => 8, 'flags' => '', ), 9 => array( 'type' => 'char', 'len' => 20, 'flags' => '', ), ), 'mssql:mssql' => array( 'clob' => 'TEXT', 'date' => 'SMALLDATETIME', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( 'ini_set("mssql.datetimeconvert", "Off");', '$dbh->query("SET DATEFORMAT ymd");', ), 0 => array( 'type' => 'int', 'len' => 4, 'flags' => 'multiple_key unique_key not_null', ), 1 => array( 'type' => 'int', 'len' => 4, 'flags' => 'primary_key not_null', ), 2 => array( 'type' => 'text', 'len' => 4096, 'flags' => '', ), 3 => array( 'type' => 'datetime', 'len' => 4, 'flags' => 'multiple_key unique_key not_null', ), 4 => array( 'type' => 'char', 'len' => 2, 'flags' => 'not_null', ), 5 => array( 'type' => 'real', 'len' => 8, 'flags' => '', ), 9 => array( 'type' => 'char', 'len' => 20, 'flags' => '', ), ), 'mysql:mysql' => array( 'clob' => 'TEXT', 'date' => 'DATE', 'dateliteral' => '', 'finds_table' => true, 'size_from_table' => true, 'handles_results' => true, 'commands' => array( ), 0 => array( 'type' => 'int', 'len' => 11, 'flags' => 'not_null multiple_key', ), 1 => array( 'type' => 'int', 'len' => 11, 'flags' => 'not_null primary_key', ), 2 => array( 'type' => 'blob', 'len' => 65535, 'flags' => 'blob', ), 3 => array( 'type' => 'date', 'len' => 10, 'flags' => 'not_null multiple_key binary', ), 4 => array( 'type' => 'string', 'len' => 2, 'flags' => 'not_null', ), 5 => array( 'type' => 'real', 'len' => 4, 'flags' => '', ), 9 => array( 'type' => 'string', 'len' => 20, 'flags' => '', ), ), 'mysqli:mysqli' => array( 'clob' => 'TEXT', 'date' => 'DATE', 'dateliteral' => '', 'finds_table' => true, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( ), 0 => array( 'type' => 'int', 'len' => 11, 'flags' => 'not_null multiple_key', ), 1 => array( 'type' => 'int', 'len' => 11, 'flags' => 'not_null primary_key', ), 2 => array( 'type' => 'blob', 'len' => 65535, 'flags' => 'blob', ), 3 => array( 'type' => 'date', 'len' => 10, 'flags' => 'not_null multiple_key binary', ), 4 => array( 'type' => 'string', 'len' => 2, 'flags' => 'not_null', ), 5 => array( 'type' => 'real', 'len' => 4, 'flags' => '', ), 9 => array( 'type' => 'string', 'len' => 20, 'flags' => '', ), ), 'oci8:oci8' => array( 'clob' => 'CLOB', 'date' => 'DATE', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( '$dbh->query("ALTER SESSION SET NLS_DATE_FORMAT = \'YYYY-MM-DD\'");', ), 0 => array( 'type' => 'NUMBER', 'len' => 22, 'flags' => 'not_null', ), 1 => array( 'type' => 'NUMBER', 'len' => 22, 'flags' => 'not_null', ), 2 => array( 'type' => 'CLOB', 'len' => 4000, 'flags' => '', ), 3 => array( 'type' => 'DATE', 'len' => 7, 'flags' => 'not_null', ), 4 => array( 'type' => 'CHAR', 'len' => 2, 'flags' => 'not_null', ), 5 => array( 'type' => 'NUMBER', 'len' => 22, 'flags' => '', ), 9 => array( 'type' => 'VARCHAR', 'len' => 20, 'flags' => '', ), ), 'odbc:access' => array( 'clob' => 'TEXT', 'date' => 'DATETIME', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( ), 0 => array( 'type' => 'INTEGER', 'len' => 10, 'flags' => 'not_null', ), 1 => array( 'type' => 'INTEGER', 'len' => 10, 'flags' => 'not_null', ), 2 => array( 'type' => 'LONGCHAR', 'len' => 255, 'flags' => '', ), 3 => array( 'type' => 'DATETIME', 'len' => 19, 'flags' => 'not_null', ), 4 => array( 'type' => 'VARCHAR', 'len' => 2, 'flags' => 'not_null', ), 5 => array( 'type' => 'DECIMAL', 'len' => 15, 'flags' => '', ), 9 => array( 'type' => 'VARCHAR', 'len' => 20, 'flags' => '', ), ), 'odbc:db2' => array( 'clob' => 'CLOB', 'date' => 'DATE', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( ), 0 => array( 'type' => 'INTEGER', 'len' => 10, 'flags' => 'not_null', ), 1 => array( 'type' => 'INTEGER', 'len' => 10, 'flags' => 'not_null', ), 2 => array( 'type' => 'CLOB', 'len' => 1048576, 'flags' => '', ), 3 => array( 'type' => 'DATE', 'len' => 10, 'flags' => 'not_null', ), 4 => array( 'type' => 'CHAR', 'len' => 2, 'flags' => 'not_null', ), 5 => array( 'type' => 'DECIMAL', 'len' => 2, 'flags' => '', ), 9 => array( 'type' => 'VARCHAR', 'len' => 20, 'flags' => '', ), ), 'pgsql:pgsql' => array( 'clob' => 'TEXT', 'date' => 'DATE', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( '$dbh->query("SET DATESTYLE = ISO");', ), 0 => array( 'type' => 'int4', 'len' => 4, 'flags' => 'not_null unique_key multiple_key', ), 1 => array( 'type' => 'int4', 'len' => 4, 'flags' => 'not_null primary_key', ), 2 => array( 'type' => 'text', 'len' => -1, 'flags' => '', ), 3 => array( 'type' => 'date', 'len' => 4, 'flags' => 'not_null unique_key multiple_key', ), 4 => array( 'type' => 'bpchar', 'len' => -1, 'flags' => 'not_null default_%20e', ), 5 => array( 'type' => 'numeric', 'len' => -1, 'flags' => '', ), 9 => array( 'type' => 'varchar', 'len' => -1, 'flags' => '', ), ), 'sqlite:sqlite' => array( 'clob' => 'CLOB', 'date' => 'DATE', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => false, 'commands' => array( ), 0 => array( 'type' => 'INTEGER', 'len' => 0, 'flags' => 'not_null', ), 1 => array( 'type' => 'INTEGER', 'len' => 0, 'flags' => 'primary_key auto_increment not_null', ), 2 => array( 'type' => 'CLOB', 'len' => 0, 'flags' => '', ), 3 => array( 'type' => 'DATE', 'len' => 0, 'flags' => 'not_null', ), 4 => array( 'type' => 'CHAR', 'len' => 2, 'flags' => 'not_null default_%20e', ), 5 => array( 'type' => 'DECIMAL', 'len' => 2, 'flags' => '', ), 9 => array( 'type' => 'VARCHAR', 'len' => 20, 'flags' => '', ), ), 'sybase:sybase' => array( 'clob' => 'TEXT', 'date' => 'SMALLDATETIME', 'dateliteral' => '', 'finds_table' => false, 'size_from_table' => false, 'handles_results' => true, 'commands' => array( '$dbh->query("SET DATEFORMAT ymd");', ), 0 => array( 'type' => 'int', 'len' => 11, 'flags' => 'multiple_key unique_key', ), 1 => array( 'type' => 'int', 'len' => 11, 'flags' => 'unique_key', ), 2 => array( 'type' => 'string', 'len' => 64512, 'flags' => '', ), 3 => array( 'type' => 'datetime', 'len' => 29, 'flags' => 'multiple_key unique_key', ), 4 => array( 'type' => 'string', 'len' => 2, 'flags' => '', ), 5 => array( 'type' => 'real', 'len' => 4, 'flags' => '', ), 9 => array( 'type' => 'string', 'len' => 20, 'flags' => '', ), ), ); $quirk_key = $dbh->phptype . ':' . $dbh->dbsyntax; if (!isset($quirks[$quirk_key])) { die("This test does not yet support $quirk_key"); } if (count($quirks[$quirk_key]['commands'])) { foreach ($quirks[$quirk_key]['commands'] as $value) { eval($value); } } $dbh->query('DELETE FROM phptest'); $dbh->query("INSERT INTO phptest VALUES (1, 'one', 'One', '2001-02-16')"); $dbh->query("INSERT INTO phptest VALUES (2, 'two', 'Two', '2001-02-15')"); $dbh->query("INSERT INTO phptest VALUES (3, 'three', 'Three', '2001-02-14')"); $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest_fk'); $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); if ($quirk_key == 'odbc:access') { $default_e = ''; $decimal = 'NUMERIC'; } elseif ($quirk_key == 'msql:msql') { $default_e = ''; $decimal = 'REAL'; } else { $default_e = "DEFAULT ' e'"; $decimal = 'DECIMAL(2,1)'; } if ($quirk_key == 'mssql:mssql') { /* We need to reset the expected length of phptest_fk.c to match * @@TEXTSIZE, since the default value seems to vary based on the version * of SQL Server we're talking to and its configuration. */ $textsize = $dbh->getOne('SELECT @@TEXTSIZE'); if (DB::isError($textsize)) { echo 'Error retrieving @@TEXTSIZE: '.$textsize->getMessage()."\n"; } else { $quirks[$quirk_key][2]['len'] = (integer) $textsize; } } // $null is set in mktable.inc switch ($dbh->phptype) { case 'msql': $dbh->query(" CREATE TABLE phptest_fk ( a INTEGER NOT NULL, fk INTEGER NOT NULL, cc {$quirks[$quirk_key]['clob']} $null, d {$quirks[$quirk_key]['date']} NOT NULL, e CHAR(2) $default_e NOT NULL, f $decimal $null ) "); $dbh->query('CREATE UNIQUE INDEX fkpk ON phptest_fk (fk)'); $dbh->query('CREATE UNIQUE INDEX fkuk ON phptest_fk (a, d)'); break; default: $dbh->query(" CREATE TABLE phptest_fk ( a INTEGER NOT NULL, fk INTEGER NOT NULL, cc {$quirks[$quirk_key]['clob']} $null, d {$quirks[$quirk_key]['date']} NOT NULL, e CHAR(2) $default_e NOT NULL, f $decimal $null, PRIMARY KEY (fk), UNIQUE (a, d) ) "); } $dbh->query("CREATE INDEX thedidx ON phptest_fk (d)"); $dbh->query("INSERT INTO phptest_fk VALUES (10, 1, 'One'," . $quirks[$quirk_key]['dateliteral'] . "'2001-02-16', 'c1', 1.1)"); $dbh->query("INSERT INTO phptest_fk VALUES (20, 2, 'Two'," . $quirks[$quirk_key]['dateliteral'] . "'2001-02-15', 'c2', 2.2)"); $dbh->query("INSERT INTO phptest_fk VALUES (30, 3, 'Three'," . $quirks[$quirk_key]['dateliteral'] . "'2001-02-14', 'c3', 3.3)"); function &runQuery() { global $dbh, $resultobj; $quirk_key = $dbh->phptype . ':' . $dbh->dbsyntax; switch ($quirk_key) { case 'odbc:db2': // can't extract blob's this way so make a fake column $query = "SELECT phptest_fk.a, phptest_fk.fk, 'tempxyz' AS cc," . ' phptest_fk.d, phptest_fk.e, phptest_fk.f,' . ' phptest.a, phptest.b, phptest.cc, phptest.d' . ' FROM phptest_fk, phptest' . ' WHERE phptest.a = phptest_fk.fk'; break; case 'msql:msql': $query = 'SELECT phptest_fk.a, phptest_fk.fk, phptest_fk.cc,' . ' phptest_fk.d, phptest_fk.e, phptest_fk.f,' . ' phptest.a, phptest.b, phptest.cc, phptest.d' . ' FROM phptest_fk, phptest' . ' WHERE phptest.a = phptest_fk.fk'; break; default: $query = 'SELECT phptest_fk.a, phptest_fk.fk, phptest_fk.cc,' . ' phptest_fk.d, phptest_fk.e, phptest_fk.f,' . ' phptest.a, phptest.b, phptest.cc, phptest.d' . ' FROM phptest_fk, phptest' . ' WHERE phptest.a = phptest_fk.fk'; } $resultobj =& $dbh->query($query); return $resultobj; } $expected01 = 'table ... matched expected value name => a type ... matched expected value len ... matched expected value flags ... matched expected value '; $expected02 = 'table ... matched expected value name => fk type ... matched expected value len ... matched expected value flags ... matched expected value '; $expected03 = 'table ... matched expected value name => cc type ... matched expected value len ... matched expected value flags ... matched expected value '; $expected04 = 'table ... matched expected value name => d type ... matched expected value len ... matched expected value flags ... matched expected value '; $expected05 = 'table ... matched expected value name => e type ... matched expected value len ... matched expected value flags ... matched expected value '; $expected06 = 'table ... matched expected value name => f type ... matched expected value len ... matched expected value flags ... matched expected value '; $expected10 = 'table ... matched expected value name => d type ... matched expected value len ... matched expected value flags ... matched expected value '; print "\n==========================================\n"; print "Passing result OBJECT to method in DB_.\n"; print "Output = default.\n"; print "------------------------------------------\n"; $resultobj =& runQuery(); // This numRows call provides a regression test for bug #4388. $resultobj->numRows(); $array = $dbh->tableInfo($resultobj); print "\ncolumn 0:\n"; examineArrayData($array, $expected01, 0); print "\ncolumn 9:\n"; examineArrayData($array, $expected10, 9); print "\n==========================================\n"; print "Passing result ID to method in DB_.\n"; print "Output = DB_TABLEINFO_ORDER.\n"; print "------------------------------------------\n"; $resultobj =& runQuery(); $array = $dbh->tableInfo($resultobj->result, DB_TABLEINFO_ORDER); print "\ncolumn 0:\n"; examineArrayData($array, $expected01, 0); print "\ncolumn 3:\n"; examineArrayData($array, $expected04, 3); print "\nnum_fields: "; if ($quirks[$quirk_key]['handles_results'] && $array['num_fields'] == 10) { print "matched expected result\n"; } elseif (DB::isError($array) && $array->getCode() == DB_ERROR_NOT_CAPABLE) { print "matched expected result\n"; } else { print "This DMBS didn't produce proper results\n"; } print "\norder:\n"; if ($quirks[$quirk_key]['handles_results'] && is_array($array['order'])) { $expected = 'a => 6 b => 7 cc => 8 d => 9 e => 4 f => 5 fk => 1 '; ksort($array['order']); examineArrayData($array['order'], $expected); } elseif (DB::isError($array) && $array->getCode() == DB_ERROR_NOT_CAPABLE) { print "matched expected result\n"; } else { print "This DMBS didn't produce proper results\n"; } print "\n==========================================\n"; print "Passing DB_TABLEINFO_ORDERTABLE to method in DB_result.\n"; print "Output = DB_TABLEINFO_ORDERTABLE.\n"; print "------------------------------------------\n"; $resultobj =& runQuery(); $array = $resultobj->tableInfo(DB_TABLEINFO_ORDERTABLE); // Free this to keep interbase happy. $resultobj->free(); print "\ncolumn 0:\n"; examineArrayData($array, $expected01, 0); print "\ncolumn 3:\n"; examineArrayData($array, $expected04, 3); print "\nnum_fields: "; if ($quirks[$quirk_key]['handles_results'] && $array['num_fields'] == 10) { print "matched expected result\n"; } elseif (DB::isError($array) && $array->getCode() == DB_ERROR_NOT_CAPABLE) { print "matched expected result\n"; } else { print "This DMBS didn't produce proper results\n"; } print 'ordertable[phptest]: '; $expected = 'a => 6 b => 7 cc => 8 d => 9 '; if ($quirks[$quirk_key]['handles_results'] && isset($array['ordertable']['phptest'])) { $actual = returnArrayData($array['ordertable']['phptest']); } else { $actual = ''; } if ($actual == $expected) { print "matched expected result\n"; } else { if (($quirks[$quirk_key]['finds_table'] === false || DB::isError($array) && $array->getCode() == DB_ERROR_NOT_CAPABLE) && $actual == '') { print "matched expected result\n"; } else { print "DIDN'T match expected values...\n"; print "~~~~~~~~\nExpected:\n$expected\n"; print "~~~~\nActual:\n$actual\n~~~~~~~~\n\n"; } } print 'ordertable[phptest_fk]: '; $expected = 'a => 0 fk => 1 cc => 2 d => 3 e => 4 f => 5 '; if ($quirks[$quirk_key]['handles_results'] && isset($array['ordertable']['phptest_fk'])) { $actual = returnArrayData($array['ordertable']['phptest_fk']); } else { $actual = ''; } if ($actual == $expected) { print "matched expected result\n"; } else { if (($quirks[$quirk_key]['finds_table'] === false || DB::isError($array) && $array->getCode() == DB_ERROR_NOT_CAPABLE) && $actual == '') { print "matched expected result\n"; } else { print "DIDN'T match expected values...\n"; print "~~~~~~~~\nExpected:\n$expected\n"; print "~~~~\nActual:\n$actual\n~~~~~~~~\n\n"; } } print "\n==========================================\n"; print "Passing TABLE NAME 'phptest_fk' to method in DB_.\n"; print "Output = default.\n"; print "------------------------------------------\n"; $array = $dbh->tableInfo('phptest_fk'); print "\ncolumn 0:\n"; examineArrayData($array, $expected01, 0, false); print "\ncolumn 1:\n"; examineArrayData($array, $expected02, 1, false); print "\ncolumn 2:\n"; examineArrayData($array, $expected03, 2, false); print "\ncolumn 3:\n"; examineArrayData($array, $expected04, 3, false); print "\ncolumn 4:\n"; examineArrayData($array, $expected05, 4, false); print "\ncolumn 5:\n"; examineArrayData($array, $expected06, 5, false); print "\n==========================================\n"; print "Passing TABLE NAME 'phptest_fk' to method in DB_.\n"; print "Output = DB_TABLEINFO_FULL.\n"; print "------------------------------------------\n"; $array = $dbh->tableInfo('phptest_fk', DB_TABLEINFO_FULL); print "\ncolumn 0:\n"; examineArrayData($array, $expected01, 0, false); print "\norder:\n"; $expect ='a => 0 fk => 1 cc => 2 d => 3 e => 4 f => 5 '; examineArrayData($array['order'], $expect, false, false); print "\nordertable[phptest_fk]:\n"; $expect ='a => 0 fk => 1 cc => 2 d => 3 e => 4 f => 5 '; examineArrayData($array['ordertable']['phptest_fk'], $expect); print "\n==========================================\n"; print "Passing TABLE NAME 'phptest_fk' to method in DB_ AGAIN.\n"; print "Output = DB_TABLEINFO_FULL, lowercasing turned off.\n"; print "------------------------------------------\n"; $dbh->setOption('portability', DB_PORTABILITY_ALL ^ DB_PORTABILITY_LOWERCASE); $array = $dbh->tableInfo('phptest_fk', DB_TABLEINFO_FULL); // testing non-lowercasing above to ensure program doesn't die. // lowercase the names here to ensure test uniformity. $array[0]['table'] = strtolower($array[0]['table']); $array[0]['name'] = strtolower($array[0]['name']); print "\ncolumn 0:\n"; examineArrayData($array, 0, false); $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); drop_table($dbh, 'phptest_fk'); ?> --EXPECT-- ========================================== Passing result OBJECT to method in DB_. Output = default. ------------------------------------------ column 0: matched expected result column 9: matched expected result ========================================== Passing result ID to method in DB_. Output = DB_TABLEINFO_ORDER. ------------------------------------------ column 0: matched expected result column 3: matched expected result num_fields: matched expected result order: matched expected result ========================================== Passing DB_TABLEINFO_ORDERTABLE to method in DB_result. Output = DB_TABLEINFO_ORDERTABLE. ------------------------------------------ column 0: matched expected result column 3: matched expected result num_fields: matched expected result ordertable[phptest]: matched expected result ordertable[phptest_fk]: matched expected result ========================================== Passing TABLE NAME 'phptest_fk' to method in DB_. Output = default. ------------------------------------------ column 0: matched expected result column 1: matched expected result column 2: matched expected result column 3: matched expected result column 4: matched expected result column 5: matched expected result ========================================== Passing TABLE NAME 'phptest_fk' to method in DB_. Output = DB_TABLEINFO_FULL. ------------------------------------------ column 0: matched expected result order: matched expected result ordertable[phptest_fk]: matched expected result ========================================== Passing TABLE NAME 'phptest_fk' to method in DB_ AGAIN. Output = DB_TABLEINFO_FULL, lowercasing turned off. ------------------------------------------ column 0: matched expected result DB-1.7.14/tests/driver/17query.phpt0000770000175100017510000001150711626162707016536 0ustar danielcdanielc--TEST-- DB_driver::query --INI-- error_reporting = 2047 --SKIPIF-- * @internal */ chdir(dirname(__FILE__)); require_once dirname(__FILE__) . '/skipif.inc'; ?> --FILE-- setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); die($o->toString()); } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $dbh->setFetchMode(DB_FETCHMODE_ASSOC); $res =& $dbh->query('DELETE FROM phptest WHERE a = 17'); print '1) delete: ' . ($res === DB_OK ? 'okay' : 'error') . "\n"; $res =& $dbh->query("INSERT INTO phptest (a, b, cc) VALUES (17, 'one', 'One')"); print '2) insert: ' . ($res === DB_OK ? 'okay' : 'error') . "\n"; $res =& $dbh->query('INSERT INTO phptest (a, b, cc) VALUES (?, ?, ?)', array(17, 'two', 'Two')); print '3) insert: ' . ($res === DB_OK ? 'okay' : 'error') . "\n"; $res =& $dbh->query('SELECT a, b FROM phptest WHERE a = 17'); $row = $res->fetchRow(); print "4) a = {$row['a']}, b = {$row['b']}\n"; $res->free(); // keep fbsql happy. $res =& $dbh->query('SELECT a, b FROM phptest WHERE cc = ?', array('Two')); $row = $res->fetchRow(); print "5) a = {$row['a']}, b = {$row['b']}\n"; $array = array( 'foo' => 11, 'bar' => 'three', 'baz' => null, ); $res =& $dbh->query('INSERT INTO phptest (a, b, d) VALUES (?, ?, ?)', $array); print '6) insert: ' . ($res === DB_OK ? 'okay' : 'error') . "\n"; $res =& $dbh->query('SELECT a, b, d FROM phptest WHERE a = ?', 11); $row = $res->fetchRow(); print "7) a = {$row['a']}, b = {$row['b']}, d = "; if ($dbh->phptype == 'msql') { if (array_key_exists('d', $row)) { $type = gettype($row['d']); if ($type == 'NULL' || $row['d'] == '') { print "got expected value\n"; } else { print "ERR: expected d's type to be NULL but it's $type and the value is "; print $row['d'] . "\n"; } } else { // http://bugs.php.net/?id=31960 print "Prior to PHP 4.3.11 or 5.0.4, PHP's msql extension silently" . " dropped columns with null values. You need to upgrade.\n"; } } else { $type = gettype($row['d']); if ($type == 'NULL' || $row['d'] == '') { print "got expected value\n"; } else { print "ERR: expected d's type to be NULL but it's $type and the value is "; print $row['d'] . "\n"; } } $res =& $dbh->query('DELETE FROM phptest WHERE a = ?', array(17)); print '8) delete: ' . ($res === DB_OK ? 'okay' : 'error') . "\n"; $res =& $dbh->query('DELETE FROM phptest WHERE a = ?', array(0)); print '9) delete with array(0) as param: ' . ($res === DB_OK ? 'okay' : 'error') . "\n"; $res =& $dbh->query('DELETE FROM phptest WHERE a = ?', 0); print '10) delete with 0 as param: ' . ($res === DB_OK ? 'okay' : 'error') . "\n"; $dbh->nextQueryIsManip(true); $res =& $dbh->query('SELECT * FROM phptest'); print '11) query is manip (with override): ' . ($dbh->_last_query_manip ? 'true' : 'false') . "\n"; $dbh->nextQueryIsManip(false); $res =& $dbh->query('SELECT * FROM phptest'); print '12) query is manip (without override): ' . ($dbh->_last_query_manip ? 'true' : 'false') . "\n"; // This one's here for bug #11716. if ($dbh->phptype == 'msql' || $dbh->phptype == 'ibase' || $dbh->phptype == 'oci8') { // Some databases don't support quoted identifiers. They are full of lose. $res =& $dbh->query('SELECT a FROM phptest'); } else { $res =& $dbh->query('SELECT '.$dbh->quoteIdentifier('a').' FROM phptest'); } print '13) select with quoteIdentifier: '; $row = $res->fetchRow(DB_FETCHMODE_ASSOC); if (isset($row['a'])) { if ($row['a'] == 42) { print "okay\n"; } else { print "field value incorrect\n"; } } else { print "expected field not in row\n"; } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); ?> --EXPECT-- 1) delete: okay 2) insert: okay 3) insert: okay 4) a = 17, b = one 5) a = 17, b = two 6) insert: okay 7) a = 11, b = three, d = got expected value 8) delete: okay 9) delete with array(0) as param: okay 10) delete with 0 as param: okay 11) query is manip (with override): true 12) query is manip (without override): false 13) select with quoteIdentifier: okay DB-1.7.14/tests/driver/18get.phpt0000770000175100017510000003653611626162707016162 0ustar danielcdanielc--TEST-- DB_driver::get --INI-- error_reporting = 2047 --SKIPIF-- * @internal */ chdir(dirname(__FILE__)); require_once dirname(__FILE__) . '/skipif.inc'; ?> --FILE-- phptest table. */ require_once dirname(__FILE__) . '/mktable.inc'; /** * Local error callback handler. * * Drops the phptest table, prints out an error message and kills the * process. * * @param object $o PEAR error object automatically passed to this method * @return void * @see PEAR::setErrorHandling() */ function pe($o){ global $dbh; $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); die($o->toString()); } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $dbh->query("INSERT INTO phptest VALUES (2, 'two', 'Two', '2002-02-22')"); $dbh->query("INSERT INTO phptest VALUES (42, 'three', 'Three', '2003-03-23')"); print "===================================================\n"; print 'testing getOne: '; $ret =& $dbh->getOne("SELECT * FROM phptest WHERE cc = 'Two'"); print_r($ret); print "\n"; print 'testing getOne with string params: '; $ret =& $dbh->getOne('SELECT * FROM phptest WHERE cc = ?', 'Three'); print_r($ret); print "\n"; print 'testing getOne with array params: '; $ret =& $dbh->getOne('SELECT * FROM phptest WHERE cc = ?', array('Two')); print_r($ret); print "\n"; print "\n===================================================\n"; print "testing getRow:\n"; $ret =& $dbh->getRow("SELECT * FROM phptest WHERE cc = 'Two'"); print_r($ret); print "testing getRow with null params, DB_FETCHMODE_ORDERED:\n"; $ret =& $dbh->getRow("SELECT * FROM phptest WHERE cc = 'Two'", null, DB_FETCHMODE_ORDERED); print_r($ret); // THIS DOESN'T WORK DUE TO BACKWARDS COMPATIBILITY CRAP // print "testing getRow with string params, DB_FETCHMODE_ORDERED:\n"; // $ret =& $dbh->getRow('SELECT * FROM phptest WHERE cc = ?', // 'Two', DB_FETCHMODE_ORDERED); // print_r($ret); // // testing getRow with string params, DB_FETCHMODE_ORDERED: // Array // ( // [0] => 2 // [1] => two // [2] => Two // [3] => 2002-02-22 // ) print "testing getRow with REVERSED args: DB_FETCHMODE_ASSOC, array params:\n"; $ret =& $dbh->getRow('SELECT * FROM phptest WHERE cc = ?', DB_FETCHMODE_ASSOC, array('Two')); print_r($ret); print "testing getRow with REVERSED args: DB_FETCHMODE_ASSOC:\n"; $ret =& $dbh->getRow("SELECT * FROM phptest WHERE cc = 'Two'", DB_FETCHMODE_ASSOC); print_r($ret); print "testing getRow with array params, DB_FETCHMODE_ASSOC:\n"; $ret =& $dbh->getRow('SELECT * FROM phptest WHERE cc = ?', array('Two'), DB_FETCHMODE_ASSOC); print_r($ret); print "testing getRow with array params, DB_FETCHMODE_OBJECT:\n"; $ret =& $dbh->getRow('SELECT * FROM phptest WHERE cc = ?', array('Two'), DB_FETCHMODE_OBJECT); print_r($ret); print "\n===================================================\n"; print "testing getCol:\n"; $ret =& $dbh->getCol("SELECT * FROM phptest ORDER BY b"); print_r($ret); print "testing getCol on query with no records:\n"; $ret =& $dbh->getCol('SELECT * FROM phptest WHERE a > 200'); print_r($ret); print "testing getCol with invalid column id:\n"; $dbh->setErrorHandling(PEAR_ERROR_RETURN); $ret =& $dbh->getCol('SELECT b FROM phptest ORDER BY b', 1); if (DB::isError($ret)) { echo $ret->getMessage() . "\n"; } else { print ">> Should have produced 'no such field' error\n"; } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); print "testing getCol with 1 col:\n"; $ret =& $dbh->getCol("SELECT * FROM phptest ORDER BY b", 1); print_r($ret); print "testing getCol with b col:\n"; $ret =& $dbh->getCol("SELECT * FROM phptest ORDER BY b", 'b'); print_r($ret); print "testing getCol with b col, scalar params:\n"; $ret =& $dbh->getCol("SELECT * FROM phptest WHERE a < ? ORDER BY b", 'b', 100); print_r($ret); print "testing getCol with b col, array params:\n"; $ret =& $dbh->getCol("SELECT * FROM phptest WHERE a < ? ORDER BY b", 'b', array(100)); print_r($ret); print "\n===================================================\n"; print "testing getAssoc:\n"; $ret =& $dbh->getAssoc('SELECT a, b, cc FROM phptest WHERE a < 100 ORDER BY b'); print_r($ret); print "testing getAssoc with false force, null params, DB_FETCHMODE_ORDERED:\n"; $ret =& $dbh->getAssoc("SELECT a, b, cc FROM phptest WHERE a < 100 ORDER BY b", false, null, DB_FETCHMODE_ORDERED); print_r($ret); print "testing getAssoc with false force, scalar params, DB_FETCHMODE_ASSOC:\n"; $ret =& $dbh->getAssoc('SELECT a, b, cc FROM phptest WHERE a < ? ORDER BY b', false, 100, DB_FETCHMODE_ASSOC); print_r($ret); print "testing getAssoc with two cols, false force, scalar params, DB_FETCHMODE_ASSOC:\n"; $ret =& $dbh->getAssoc('SELECT a, b FROM phptest WHERE a < ? ORDER BY b', false, 100, DB_FETCHMODE_ASSOC); print_r($ret); print "testing getAssoc with two cols, true force, scalar params, DB_FETCHMODE_ASSOC:\n"; $ret =& $dbh->getAssoc('SELECT a, b FROM phptest WHERE a < ? ORDER BY b', true, 100, DB_FETCHMODE_ASSOC); print_r($ret); print "testing getAssoc with false force, scalar params, DB_FETCHMODE_ASSOC, true group:\n"; $ret =& $dbh->getAssoc('SELECT a, b, cc FROM phptest WHERE a < ? ORDER BY b', false, 100, DB_FETCHMODE_ASSOC, true); print_r($ret); print "testing getAssoc with false force, array params, DB_FETCHMODE_OBJECT:\n"; $ret =& $dbh->getAssoc('SELECT a, b, cc FROM phptest WHERE a < ? ORDER BY b', false, array(100), DB_FETCHMODE_OBJECT); print_r($ret); print "testing getAssoc with true force, array params, DB_FETCHMODE_OBJECT, true group:\n"; $ret =& $dbh->getAssoc('SELECT a, b, cc FROM phptest WHERE a < ? ORDER BY b', false, array(100), DB_FETCHMODE_OBJECT, true); print_r($ret); print "\n===================================================\n"; print "testing getAll:\n"; $ret =& $dbh->getAll("SELECT * FROM phptest WHERE cc = 'Two' OR cc = 'Three'"); print_r($ret); print "testing getAll with null params, DB_FETCHMODE_ORDERED:\n"; $ret =& $dbh->getAll("SELECT * FROM phptest WHERE cc = 'Two' OR cc = 'Three'", null, DB_FETCHMODE_ORDERED); print_r($ret); // THIS DOESN'T WORK DUE TO BACKWARDS COMPATIBILITY CRAP // print "testing getAll with string params, DB_FETCHMODE_ORDERED:\n"; // $ret =& $dbh->getAll('SELECT * FROM phptest WHERE cc = ?', // 'Two', DB_FETCHMODE_ORDERED); // print_r($ret); // // testing getAll with string params, DB_FETCHMODE_ORDERED: // Array // ( // [0] => 2 // [1] => two // [2] => Two // [3] => 2002-02-22 // ) print "testing getAll with REVERSED args: DB_FETCHMODE_ASSOC, array params:\n"; $ret =& $dbh->getAll('SELECT * FROM phptest WHERE cc = ? OR cc = ? ORDER BY cc', DB_FETCHMODE_ASSOC, array('Two', 'Three')); print_r($ret); print "testing getAll with REVERSED args: DB_FETCHMODE_ASSOC:\n"; $ret =& $dbh->getAll("SELECT * FROM phptest WHERE cc = 'Two' OR cc = 'Three'", DB_FETCHMODE_ASSOC); print_r($ret); print "testing getAll with array params, DB_FETCHMODE_ASSOC:\n"; $ret =& $dbh->getAll('SELECT * FROM phptest WHERE cc = ? OR cc = ? ORDER BY cc', array('Two', 'Three'), DB_FETCHMODE_ASSOC); print_r($ret); print "testing getAll with array params, DB_FETCHMODE_OBJECT:\n"; $ret =& $dbh->getAll('SELECT * FROM phptest WHERE cc = ? OR cc = ? ORDER BY cc', array('Two', 'Three'), DB_FETCHMODE_OBJECT); print_r($ret); print "\n===================================================\n"; print 'testing getOne with null value in column: '; $dbh->query("INSERT INTO phptest VALUES (9, 'nine', '', NULL)"); $ret =& $dbh->getOne('SELECT d FROM phptest WHERE a = 9'); if ($ret === '') { print "matches expected result\n"; } else { if ($dbh->phptype == 'msql') { if (gettype($ret) == 'NULL') { // msql doesn't even return the column. Joy! :) // http://bugs.php.net/?id=31960 print "matches expected result\n"; } else { print "WOW, mSQL now returns columns that have NULLS in them\n"; } } else { print 'type=' . gettype($ret) . ", value=$ret\n"; } } print 'testing getOne with empty string in column: '; $ret =& $dbh->getOne('SELECT cc FROM phptest WHERE a = 9'); if ($ret === '') { print "empty string\n"; } else { print 'type=' . gettype($ret) . ", value=$ret\n"; } print "\n===================================================\n"; drop_table($dbh, 'phptest'); ?> --EXPECT-- =================================================== testing getOne: 2 testing getOne with string params: 42 testing getOne with array params: 2 =================================================== testing getRow: Array ( [0] => 2 [1] => two [2] => Two [3] => 2002-02-22 ) testing getRow with null params, DB_FETCHMODE_ORDERED: Array ( [0] => 2 [1] => two [2] => Two [3] => 2002-02-22 ) testing getRow with REVERSED args: DB_FETCHMODE_ASSOC, array params: Array ( [a] => 2 [b] => two [cc] => Two [d] => 2002-02-22 ) testing getRow with REVERSED args: DB_FETCHMODE_ASSOC: Array ( [a] => 2 [b] => two [cc] => Two [d] => 2002-02-22 ) testing getRow with array params, DB_FETCHMODE_ASSOC: Array ( [a] => 2 [b] => two [cc] => Two [d] => 2002-02-22 ) testing getRow with array params, DB_FETCHMODE_OBJECT: stdClass Object ( [a] => 2 [b] => two [cc] => Two [d] => 2002-02-22 ) =================================================== testing getCol: Array ( [0] => 42 [1] => 42 [2] => 2 ) testing getCol on query with no records: Array ( ) testing getCol with invalid column id: DB Error: no such field testing getCol with 1 col: Array ( [0] => bing [1] => three [2] => two ) testing getCol with b col: Array ( [0] => bing [1] => three [2] => two ) testing getCol with b col, scalar params: Array ( [0] => bing [1] => three [2] => two ) testing getCol with b col, array params: Array ( [0] => bing [1] => three [2] => two ) =================================================== testing getAssoc: Array ( [42] => Array ( [0] => three [1] => Three ) [2] => Array ( [0] => two [1] => Two ) ) testing getAssoc with false force, null params, DB_FETCHMODE_ORDERED: Array ( [42] => Array ( [0] => three [1] => Three ) [2] => Array ( [0] => two [1] => Two ) ) testing getAssoc with false force, scalar params, DB_FETCHMODE_ASSOC: Array ( [42] => Array ( [b] => three [cc] => Three ) [2] => Array ( [b] => two [cc] => Two ) ) testing getAssoc with two cols, false force, scalar params, DB_FETCHMODE_ASSOC: Array ( [42] => three [2] => two ) testing getAssoc with two cols, true force, scalar params, DB_FETCHMODE_ASSOC: Array ( [42] => Array ( [b] => three ) [2] => Array ( [b] => two ) ) testing getAssoc with false force, scalar params, DB_FETCHMODE_ASSOC, true group: Array ( [42] => Array ( [0] => Array ( [b] => bing [cc] => This is a test ) [1] => Array ( [b] => three [cc] => Three ) ) [2] => Array ( [0] => Array ( [b] => two [cc] => Two ) ) ) testing getAssoc with false force, array params, DB_FETCHMODE_OBJECT: Array ( [42] => stdClass Object ( [a] => 42 [b] => three [cc] => Three ) [2] => stdClass Object ( [a] => 2 [b] => two [cc] => Two ) ) testing getAssoc with true force, array params, DB_FETCHMODE_OBJECT, true group: Array ( [42] => Array ( [0] => stdClass Object ( [a] => 42 [b] => bing [cc] => This is a test ) [1] => stdClass Object ( [a] => 42 [b] => three [cc] => Three ) ) [2] => Array ( [0] => stdClass Object ( [a] => 2 [b] => two [cc] => Two ) ) ) =================================================== testing getAll: Array ( [0] => Array ( [0] => 2 [1] => two [2] => Two [3] => 2002-02-22 ) [1] => Array ( [0] => 42 [1] => three [2] => Three [3] => 2003-03-23 ) ) testing getAll with null params, DB_FETCHMODE_ORDERED: Array ( [0] => Array ( [0] => 2 [1] => two [2] => Two [3] => 2002-02-22 ) [1] => Array ( [0] => 42 [1] => three [2] => Three [3] => 2003-03-23 ) ) testing getAll with REVERSED args: DB_FETCHMODE_ASSOC, array params: Array ( [0] => Array ( [a] => 42 [b] => three [cc] => Three [d] => 2003-03-23 ) [1] => Array ( [a] => 2 [b] => two [cc] => Two [d] => 2002-02-22 ) ) testing getAll with REVERSED args: DB_FETCHMODE_ASSOC: Array ( [0] => Array ( [a] => 2 [b] => two [cc] => Two [d] => 2002-02-22 ) [1] => Array ( [a] => 42 [b] => three [cc] => Three [d] => 2003-03-23 ) ) testing getAll with array params, DB_FETCHMODE_ASSOC: Array ( [0] => Array ( [a] => 42 [b] => three [cc] => Three [d] => 2003-03-23 ) [1] => Array ( [a] => 2 [b] => two [cc] => Two [d] => 2002-02-22 ) ) testing getAll with array params, DB_FETCHMODE_OBJECT: Array ( [0] => stdClass Object ( [a] => 42 [b] => three [cc] => Three [d] => 2003-03-23 ) [1] => stdClass Object ( [a] => 2 [b] => two [cc] => Two [d] => 2002-02-22 ) ) =================================================== testing getOne with null value in column: matches expected result testing getOne with empty string in column: empty string =================================================== DB-1.7.14/tests/driver/19getlistof.phpt0000770000175100017510000001751711626162707017402 0ustar danielcdanielc--TEST-- DB_driver::getListOf --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- array( 'dbase:dbase' => DB_ERROR_UNSUPPORTED, 'fbsql:fbsql' => 'array', 'ibase:ibase' => 'array', 'ibase:firebird' => 'array', 'ifx:ifx' => 'array', 'msql:msql' => 'array', 'mssql:mssql' => 'array', 'mysql:mysql' => 'array', 'mysqli:mysqli' => 'array', 'oci8:oci8' => 'array', 'odbc:access' => 'array', 'odbc:db2' => 'array', 'pgsql:pgsql' => 'array', 'sqlite:sqlite' => 'array', 'sybase:sybase' => 'array', ), 'views' => array( 'dbase:dbase' => DB_ERROR_UNSUPPORTED, 'fbsql:fbsql' => 'array', 'ibase:ibase' => 'array', 'ibase:firebird' => 'array', 'ifx:ifx' => DB_ERROR_UNSUPPORTED, 'msql:msql' => DB_ERROR_UNSUPPORTED, 'mssql:mssql' => 'array', 'mysql:mysql' => DB_ERROR_UNSUPPORTED, 'mysqli:mysqli' => DB_ERROR_UNSUPPORTED, 'oci8:oci8' => 'array', 'odbc:access' => 'array', 'odbc:db2' => 'array', 'pgsql:pgsql' => 'array', 'sqlite:sqlite' => DB_ERROR_UNSUPPORTED, 'sybase:sybase' => 'array', ), 'users' => array( 'dbase:dbase' => DB_ERROR_UNSUPPORTED, 'fbsql:fbsql' => 'array', 'ibase:ibase' => 'array', 'ibase:firebird' => 'array', 'ifx:ifx' => DB_ERROR_UNSUPPORTED, 'msql:msql' => DB_ERROR_UNSUPPORTED, 'mssql:mssql' => DB_ERROR_UNSUPPORTED, 'mysql:mysql' => DB_ERROR_ACCESS_VIOLATION, 'mysqli:mysqli' => DB_ERROR_ACCESS_VIOLATION, 'oci8:oci8' => DB_ERROR_UNSUPPORTED, 'odbc:access' => DB_ERROR_UNSUPPORTED, 'odbc:db2' => DB_ERROR_UNSUPPORTED, 'pgsql:pgsql' => 'array', 'sqlite:sqlite' => DB_ERROR_UNSUPPORTED, 'sybase:sybase' => DB_ERROR_UNSUPPORTED, ), 'databases' => array( 'dbase:dbase' => DB_ERROR_UNSUPPORTED, 'fbsql:fbsql' => DB_ERROR_UNSUPPORTED, 'ibase:ibase' => DB_ERROR_UNSUPPORTED, 'ibase:firebird' => DB_ERROR_UNSUPPORTED, 'ifx:ifx' => DB_ERROR_UNSUPPORTED, 'msql:msql' => 'array', 'mssql:mssql' => DB_ERROR_UNSUPPORTED, 'mysql:mysql' => 'array', 'mysqli:mysqli' => 'array', 'oci8:oci8' => DB_ERROR_UNSUPPORTED, 'odbc:access' => 'array', 'odbc:db2' => 'array', 'pgsql:pgsql' => 'array', 'sqlite:sqlite' => DB_ERROR_UNSUPPORTED, 'sybase:sybase' => DB_ERROR_UNSUPPORTED, ), 'functions' => array( 'dbase:dbase' => DB_ERROR_UNSUPPORTED, 'fbsql:fbsql' => 'array', 'ibase:ibase' => DB_ERROR_UNSUPPORTED, 'ibase:firebird' => DB_ERROR_UNSUPPORTED, 'ifx:ifx' => DB_ERROR_UNSUPPORTED, 'msql:msql' => DB_ERROR_UNSUPPORTED, 'mssql:mssql' => DB_ERROR_UNSUPPORTED, 'mysql:mysql' => DB_ERROR_UNSUPPORTED, 'mysqli:mysqli' => DB_ERROR_UNSUPPORTED, 'oci8:oci8' => DB_ERROR_UNSUPPORTED, 'odbc:access' => DB_ERROR_UNSUPPORTED, 'odbc:db2' => DB_ERROR_UNSUPPORTED, 'pgsql:pgsql' => 'array', 'sqlite:sqlite' => DB_ERROR_UNSUPPORTED, 'sybase:sybase' => DB_ERROR_UNSUPPORTED, ), 'procedures' => array( 'dbase:dbase' => DB_ERROR_UNSUPPORTED, 'fbsql:fbsql' => 'array', 'ibase:ibase' => DB_ERROR_UNSUPPORTED, 'ibase:firebird' => DB_ERROR_UNSUPPORTED, 'ifx:ifx' => DB_ERROR_UNSUPPORTED, 'msql:msql' => DB_ERROR_UNSUPPORTED, 'mssql:mssql' => DB_ERROR_UNSUPPORTED, 'mysql:mysql' => DB_ERROR_UNSUPPORTED, 'mysqli:mysqli' => DB_ERROR_UNSUPPORTED, 'oci8:oci8' => DB_ERROR_UNSUPPORTED, 'odbc:access' => DB_ERROR_UNSUPPORTED, 'odbc:db2' => DB_ERROR_UNSUPPORTED, 'pgsql:pgsql' => 'array', 'sqlite:sqlite' => DB_ERROR_UNSUPPORTED, 'sybase:sybase' => DB_ERROR_UNSUPPORTED, ), 'schema.tables' => array( 'dbase:dbase' => DB_ERROR_UNSUPPORTED, 'fbsql:fbsql' => DB_ERROR_UNSUPPORTED, 'ibase:ibase' => DB_ERROR_UNSUPPORTED, 'ibase:firebird' => DB_ERROR_UNSUPPORTED, 'ifx:ifx' => DB_ERROR_UNSUPPORTED, 'msql:msql' => DB_ERROR_UNSUPPORTED, 'mssql:mssql' => DB_ERROR_UNSUPPORTED, 'mysql:mysql' => DB_ERROR_UNSUPPORTED, 'mysqli:mysqli' => DB_ERROR_UNSUPPORTED, 'oci8:oci8' => DB_ERROR_UNSUPPORTED, 'odbc:access' => 'array', 'odbc:db2' => 'array', 'pgsql:pgsql' => 'array', 'sqlite:sqlite' => DB_ERROR_UNSUPPORTED, 'sybase:sybase' => DB_ERROR_UNSUPPORTED, ), 'schema.views' => array( 'dbase:dbase' => DB_ERROR_UNSUPPORTED, 'fbsql:fbsql' => DB_ERROR_UNSUPPORTED, 'ibase:ibase' => DB_ERROR_UNSUPPORTED, 'ibase:firebird' => DB_ERROR_UNSUPPORTED, 'ifx:ifx' => DB_ERROR_UNSUPPORTED, 'msql:msql' => DB_ERROR_UNSUPPORTED, 'mssql:mssql' => DB_ERROR_UNSUPPORTED, 'mysql:mysql' => DB_ERROR_UNSUPPORTED, 'mysqli:mysqli' => DB_ERROR_UNSUPPORTED, 'oci8:oci8' => DB_ERROR_UNSUPPORTED, 'odbc:access' => DB_ERROR_UNSUPPORTED, 'odbc:db2' => DB_ERROR_UNSUPPORTED, 'pgsql:pgsql' => 'array', 'sqlite:sqlite' => DB_ERROR_UNSUPPORTED, 'sybase:sybase' => DB_ERROR_UNSUPPORTED, ), 'synonyms' => array( 'dbase:dbase' => DB_ERROR_UNSUPPORTED, 'fbsql:fbsql' => DB_ERROR_UNSUPPORTED, 'ibase:ibase' => DB_ERROR_UNSUPPORTED, 'ibase:firebird' => DB_ERROR_UNSUPPORTED, 'ifx:ifx' => DB_ERROR_UNSUPPORTED, 'msql:msql' => DB_ERROR_UNSUPPORTED, 'mssql:mssql' => DB_ERROR_UNSUPPORTED, 'mysql:mysql' => DB_ERROR_UNSUPPORTED, 'mysqli:mysqli' => DB_ERROR_UNSUPPORTED, 'oci8:oci8' => 'array', 'odbc:access' => DB_ERROR_UNSUPPORTED, 'odbc:db2' => DB_ERROR_UNSUPPORTED, 'pgsql:pgsql' => DB_ERROR_UNSUPPORTED, 'sqlite:sqlite' => DB_ERROR_UNSUPPORTED, 'sybase:sybase' => DB_ERROR_UNSUPPORTED, ), ); /** * Determine if the output from the driver matches what we expect * * If things are as we expect, nothing is printed out. * * If things go wrong, print "UNEXPECTED OUTCOME" and display * what happened. * * @param mixed $result the result from getListOf * @param mixed $expected the expected result * @param string $name the name of the current test * * @return void */ function check_output($result, $expected, $name) { if (is_object($result)) { if ($result->getCode() !== $expected) { echo "UNEXPECTED OUTCOME FOR $name...\n"; echo $result->getDebugInfo() . "\n"; } } else { $type = gettype($result); if ($type != $expected) { if ($expected === DB_ERROR_ACCESS_VIOLATION && $type == 'array') { // This user has access to the mysql table. // Not a problem } else { echo "UNEXPECTED OUTCOME FOR $name...\n"; echo " Expected: $expected\n"; echo ' Result: '; print_r($result); echo "\n"; } } } } $dbh->setErrorHandling(PEAR_ERROR_RETURN); foreach ($tests as $test => $dbms) { check_output($dbh->getListOf($test), $dbms[$dbh->phptype . ':' . $dbh->dbsyntax], $test); } drop_table($dbh, 'phptest'); ?> --EXPECT-- DB-1.7.14/tests/driver/20locale.phpt0000660000175100017510000000362511626162707016622 0ustar danielcdanielc--TEST-- DB_driver::locale --INI-- error_reporting = 2047 --SKIPIF-- --FILE-- phptype == 'odbc') { if ($dbh->dbsyntax == 'odbc') { $type = $dbh->phptype; } else { $type = $dbh->dbsyntax; } } else { $type = $dbh->phptype; } switch ($type) { case 'access': $decimal = 'SINGLE'; break; case 'db2': case 'ibase': $decimal = 'DECIMAL(3,1)'; break; case 'ifx': // doing this for ifx to keep certain versions happy $decimal = 'DECIMAL(3,1)'; break; case 'msql': $decimal = 'REAL'; break; case 'fbsql': case 'oci8': $decimal = 'DECIMAL(3,1)'; break; default: $decimal = 'DECIMAL(3,1)'; } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'localetest'); $res = $dbh->query("CREATE TABLE localetest (a $decimal)"); if (DB::isError($res)) { echo 'Unable to create table: '.$res->getMessage()."\n"; } setlocale(LC_NUMERIC, 'de_DE'); $res = $dbh->query('INSERT INTO localetest (a) VALUES (?)', array(42.2)); if (DB::isError($res)) { echo 'Error inserting record: '.$res->getMessage()."\n"; var_dump($res); } setlocale(LC_NUMERIC, 'en_AU'); $res = $dbh->query('INSERT INTO localetest (a) VALUES (?)', array(42.2)); if (DB::isError($res)) { echo 'Error inserting record: '.$res->getMessage()."\n"; var_dump($res); } $res = $dbh->query('SELECT * FROM localetest'); if (DB::isError($res)) { echo 'Error retrieving count: '.$res->getMessage()."\n"; var_dump($res); } else { echo 'Got '.$res->numRows()." records.\n"; } drop_table($dbh, 'localetest'); ?> --EXPECT-- Got 2 records. DB-1.7.14/tests/driver/21freeResult.phpt0000660000175100017510000000143711626162707017503 0ustar danielcdanielc--TEST-- DB_driver::freeResult --INI-- error_reporting = 2047 --SKIPIF-- phptype == 'mysqli') die ('skip mysqli returns result objects rather than resources'); ?> --FILE-- query('SELECT * FROM phptest'); if (DB::isError($res)) { echo "Result is a DB_Error.\n"; } if (is_resource($res->result)) { echo "Result includes resource.\n"; } else { echo "Result does not include a resource!\n"; print_r($res->result); } if ($dbh->freeResult($res->result)) { echo "Resource was freed successfully.\n"; } else { echo "Error freeing result.\n"; } drop_table($dbh, 'phptest'); ?> --EXPECT-- Result includes resource. Resource was freed successfully. DB-1.7.14/tests/driver/connect.inc0000660000175100017510000000242011626162707016440 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: connect.inc 284375 2009-07-19 16:26:16Z danielc $ * @link http://pear.php.net/package/DB */ error_reporting(E_ALL); // Setting of $options and requiring DB are done in setup.inc /** * Establish the include_path, DSN's and connection $options */ require_once dirname(__FILE__) . '/setup.inc'; if (empty($dsns)) { die('At least one element of $dsns must be defined in setup.inc'); } list($dbms, $dsn) = each($dsns); if ($dbms == 'mssql') { ini_set('mssql.textlimit', 4096); ini_set('mssql.textsize', 4096); } $dbh =& DB::connect($dsn, $options); if (DB::isError($dbh)) { die('connect.inc: ' . $dbh->toString()); } DB-1.7.14/tests/driver/droptable.inc0000770000175100017510000000235511626162707016774 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: droptable.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Drops the requested table * * @param object $dbh the PEAR DB object currently in use * @param string $table the name of the table to be dropped * * @return int DB_OK on success. DB_Error object on failure. */ function drop_table($dbh, $table) { switch ($dbh->phptype) { case 'fbsql': $res = $dbh->query("DROP TABLE $table CASCADE"); break; default: $res = $dbh->query("DROP TABLE $table"); } return $res; } DB-1.7.14/tests/driver/mktable.inc0000660000175100017510000001023111626162707016425 0ustar danielcdanielcphptest table * * Tries to drop the table first, in case it already exists. * *
 * CREATE TABLE phptest (
 *   a INTEGER NULL,
 *   b CHAR(40) DEFAULT 'def' NOT NULL,
 *   cc VARCHAR(255) NULL,
 *   d VARCHAR(20) NULL)
 * 
* * Need NOT NULL on b to test * DB_PORTABILITY_RTRIM. MS SQL and Sybase trim output from * VARCHAR, but not on CHAR. * * Need DEFAULT value on b because Oracle considers * an empty string to be NULL. * * In Oracle, when using placeholders in WHERE clauses on * CHAR columns, the column must have RTRIM() run on * the column: * * SELECT * FROM phptest WHERE RTRIM(b) = ? * * * PHP versions 4 and 5 * * LICENSE: This source file is subject to version 3.0 of the PHP license * that is available through the world-wide-web at the following URI: * http://www.php.net/license/3_0.txt. If you did not receive a copy of * the PHP License and are unable to obtain it through the web, please * send a note to license@php.net so we can mail you a copy immediately. * * @category Database * @package DB * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: mktable.inc 284375 2009-07-19 16:26:16Z danielc $ * @link http://pear.php.net/package/DB */ /** * Establishes the DB object and connects to the database */ require_once dirname(__FILE__) . '/connect.inc'; /** * Get the drop_table() function */ require_once dirname(__FILE__) . '/droptable.inc'; /** * The error handler for the drop table procedure * * Prints out an error message and dies. */ function debug_die($o){ die($o->toString()); } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); //$dbh->setErrorHandling(PEAR_ERROR_TRIGGER); $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'debug_die'); if ($dbh->phptype == 'odbc') { if ($dbh->dbsyntax == 'odbc') { $type = $dbh->phptype; } else { $type = $dbh->dbsyntax; } } else { $type = $dbh->phptype; } switch ($type) { case 'access': $null = 'NULL'; $chrc = 'VARCHAR(255)'; $chrd = 'VARCHAR(20)'; $default = ''; $tabletype = ''; break; case 'db2': case 'ibase': $null = ''; $chrc = 'VARCHAR(255)'; $chrd = 'VARCHAR(20)'; $default = "DEFAULT 'def' NOT NULL"; $tabletype = ''; break; case 'fbsql': $null = ''; $chrc = 'CHAR(255)'; $chrd = 'CHAR(20)'; $default = "DEFAULT 'def' NOT NULL"; $date_literal = ' DATE '; $tabletype = ''; break; case 'ifx': // doing this for ifx to keep certain versions happy $null = ''; $chrc = 'CHAR(255)'; $chrd = 'CHAR(20)'; $default = "DEFAULT 'def' NOT NULL"; $tabletype = ''; break; case 'msql': $null = ''; $chrc = 'CHAR(255)'; $chrd = 'CHAR(20)'; $default = ''; $tabletype = ''; break; case 'mysql': case 'mysqli': $null = 'NULL'; $chrc = 'VARCHAR(255)'; $chrd = 'VARCHAR(20)'; $default = "DEFAULT 'def' NOT NULL"; if (!empty($needinnodb)) { $tabletype = 'TYPE=INNODB'; } else { $tabletype = ''; } break; default: $null = 'NULL'; $chrc = 'VARCHAR(255)'; $chrd = 'VARCHAR(20)'; $default = "DEFAULT 'def' NOT NULL"; $tabletype = ''; } switch ($dbh->phptype) { case 'dbase': // file exists or was created in DB_dbase::connect() break; default: $test_mktable_query = " CREATE TABLE phptest ( a INTEGER $null, b CHAR(40) $default, cc $chrc $null, d $chrd $null) $tabletype "; } $dbh->query($test_mktable_query); $dbh->query("INSERT INTO phptest VALUES(42, 'bing', 'This is a test', '1999-11-21')"); $dbh->setErrorHandling(PEAR_ERROR_RETURN); DB-1.7.14/tests/driver/multiconnect.php0000770000175100017510000000513011626162707017534 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: multiconnect.php 284375 2009-07-19 16:26:16Z danielc $ * @link http://pear.php.net/package/DB * @since File available since Release 1.7.0 */ /** * Establish the include_path, DSN's and connection $options */ require_once dirname(__FILE__) . '/setup.inc'; foreach ($dsns as $dbms => $dsn) { echo "======== $dbms ========\n"; $options['persistent'] = false; $dbh =& DB::connect($dsn, $options); if (DB::isError($dbh)) { echo 'PROBLEM: ' . $dbh->getUserInfo() . "\n"; continue; } if ($dbh->provides('new_link') && version_compare(phpversion(), $dbh->provides('new_link'), '>=')) { $probs = false; $dsn = DB::parseDSN($dsn); $dsn['new_link'] = true; $dbh =& DB::connect($dsn, $options); if (DB::isError($dbh)) { echo 'NEW LINK PROBLEM: ' . $dbh->getUserInfo() . "\n"; $probs = true; } if ($dbh->provides('pconnect')) { $options['persistent'] = true; $dbh->disconnect(); $dbh =& DB::connect($dsn, $options); if (DB::isError($dbh)) { echo 'PERSIST NEWCON PROBLEM: ' . $dbh->getUserInfo() . "\n"; $probs = true; } unset($dsn['new_link']); $dbh->disconnect(); $dbh =& DB::connect($dsn, $options); if (DB::isError($dbh)) { echo 'PERSIST OLDCON PROBLEM: ' . $dbh->getUserInfo() . "\n"; $probs = true; } } if ($probs) { continue; } $dbh->disconnect(); } elseif ($dbh->provides('pconnect')) { $options['persistent'] = true; $dbh->disconnect(); $dbh =& DB::connect($dsn, $options); if (DB::isError($dbh)) { echo 'PERSIST PROBLEM: ' . $dbh->getUserInfo() . "\n"; continue; } $dbh->disconnect(); } echo "GOOD\n"; } DB-1.7.14/tests/driver/run.cvs0000770000175100017510000000152611626162707015645 0ustar danielcdanielc#! /bin/sh # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv # PEAR DB TEST STARTER # # To run all tests: ./run # To run one test: ./run # Example: ./run db_parsedsn.phpt # # Before running the tests you must adjust the # following three variables: # The path and name of your run-tests.php file: DB_TEST_RUN_TESTS=c:/progra~1/php/run-tests.php # The path and name of your PHP CLI executable # (example c:/progra~1/php.exe): TEST_PHP_EXECUTABLE=c:/progra~1/php.exe # The full path to the present directory # (not using $PWD due to Cygwin): DB_TEST_DIR=d:/peartest/pear/DB/tests/driver # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ export TEST_PHP_EXECUTABLE if [ $# -gt 0 ] then test=$1 else test=*.phpt fi $TEST_PHP_EXECUTABLE $DB_TEST_RUN_TESTS $DB_TEST_DIR/${test} DB-1.7.14/tests/driver/setup.inc0000660000175100017510000000753011626162707016156 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: setup.inc 315483 2011-08-25 15:46:09Z danielc $ * @link http://pear.php.net/package/DB */ if (!defined('PATH_SEPARATOR')) { if (stristr(PHP_OS, 'WIN')) { /** * The string used to delimit elements of the path. */ define('PATH_SEPARATOR', ';'); } else { /** * The string used to delimit elements of the path. */ define('PATH_SEPARATOR', ':'); } } /* * If the path to your PEAR installation is found in the left hand * portion of the if() expression below, that means this file has * come from the PEAR installer. Therefore, let's use the * installed version of DB, which should be found via the * computer's default include_path. Add '.' to the include_path * to ensure '.' is in there. * * If the path has not been substituted in the if() expression, * this file has likely come from a CVS checkout or a .tar file. * Therefore, we'll assume the tests should use the version of * DB that has come from there as well. */ if ('@include_path@' != '@'.'include_path'.'@') { ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . '.' ); } else { ini_set('include_path', realpath(dirname(__FILE__) . '/../..') . PATH_SEPARATOR . '.' . PATH_SEPARATOR . ini_get('include_path') ); } /** * Grab the PEAR DB classes. */ require_once 'DB.php'; // Options used when connecting $options = array( //'optimize' => 'portability', 'portability' => DB_PORTABILITY_ALL, 'debug' => 2, ); $dbasedsn = array( 'phptype' => 'dbase', 'database' => '/path/and/name/of/dbase/file', 'mode' => 2, 'fields' => array( array('a', 'N', 5, 0), array('b', 'C', 40), array('c', 'C', 255), array('d', 'C', 20), ), ); /* * Uncomment at least one of the following elements. * When running the .phpt tests, the first uncommented element is used. * When running the multiconnect.php test, all uncommented elements are used. */ $dsns = array( // 'dbase' => $dbasedsn, // 'fbsql' => 'fbsql://_system:@/db', // 'firebird' => 'ibase(firebird)://SYSDBA:masterkey@//opt/interbase/examples/employee.gdb?dialect=3', // 'ifx' => 'ifx://user:pw@localhost/db', // 'msql' => 'msql:///db', // It's advisable to use only one of the following at a time: // 'mssql' => 'mssql://sa@somehost/pubs', // 'sybase' => 'sybase://sa@somehost/pubs', // 'mysql' => 'mysql://root@localhost/test', // 'mysqli' => 'mysqli://root@localhost/test', // 'oci8' => 'oci8://system:manager@', // 'access' => 'odbc(access)://admin@/SystemDsnName', // 'db2' => 'odbc(db2)://db2inst1:XXXX@/SAMPLE', // 'pgsql' => 'pgsql://postgres@localhost/test', 'sqlite' => 'sqlite://dummy:@localhost/' . getcwd() . DIRECTORY_SEPARATOR . 'test.db?mode=0644', ); DB-1.7.14/tests/driver/skipif.inc0000660000175100017510000000215511626162707016301 0ustar danielcdanielc * @copyright 1997-2006 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: skipif.inc 284375 2009-07-19 16:26:16Z danielc $ * @link http://pear.php.net/package/DB */ /** * Establish the include_path, DSN's and connection $options */ require_once dirname(__FILE__) . '/setup.inc'; if (empty($dsns)) { die('skip At least one element of $dsns must be defined in setup.inc'); } list($dbms, $dsn) = each($dsns); $dbh =& DB::connect($dsn, $options); if (DB::isError($dbh)) { die('skip ' . $dbh->getUserInfo()); } DB-1.7.14/tests/db_error.phpt0000660000175100017510000000616311626162707015524 0ustar danielcdanielc--TEST-- DB::DB_Error --SKIPIF-- --FILE-- 'Error', E_WARNING => 'Warning', E_PARSE => 'Parsing Error', E_NOTICE => 'Notice', E_CORE_ERROR => 'Core Error', E_CORE_WARNING => 'Core Warning', E_COMPILE_ERROR => 'Compile Error', E_COMPILE_WARNING => 'Compile Warning', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_STRICT => 'Strict Notice', ); $prefix = $errortype[$errno]; print "\n$prefix: $errmsg in " . basename($file) . " on line XXX\n"; } error_reporting(E_ALL); set_error_handler('test_error_handler'); print "testing different error codes...\n"; $e = new DB_Error(); print strtolower($e->toString())."\n"; $e = new DB_Error("test error"); print strtolower($e->toString())."\n"; $e = new DB_Error(DB_OK); print strtolower($e->toString())."\n"; $e = new DB_Error(DB_ERROR); print strtolower($e->toString())."\n"; $e = new DB_Error(DB_ERROR_SYNTAX); print strtolower($e->toString())."\n"; $e = new DB_Error(DB_ERROR_DIVZERO); print strtolower($e->toString())."\n"; print "testing different error modes...\n"; $e = new DB_Error(DB_ERROR, PEAR_ERROR_PRINT); print strtolower($e->toString())."\n"; $e = new DB_Error(DB_ERROR_SYNTAX, PEAR_ERROR_TRIGGER); print "testing different error serverities...\n"; $e = new DB_Error(DB_ERROR_SYNTAX, PEAR_ERROR_TRIGGER, E_USER_NOTICE); $e = new DB_Error(DB_ERROR_SYNTAX, PEAR_ERROR_TRIGGER, E_USER_WARNING); $e = new DB_Error(DB_ERROR_SYNTAX, PEAR_ERROR_TRIGGER, E_USER_ERROR); ?> --GET-- --POST-- --EXPECT-- testing different error codes... [db_error: message="db error: unknown error" code=-1 mode=return level=notice prefix="" info=""] [db_error: message="db error: test error" code=-1 mode=return level=notice prefix="" info=""] [db_error: message="db error: no error" code=1 mode=return level=notice prefix="" info=""] [db_error: message="db error: unknown error" code=-1 mode=return level=notice prefix="" info=""] [db_error: message="db error: syntax error" code=-2 mode=return level=notice prefix="" info=""] [db_error: message="db error: division by zero" code=-13 mode=return level=notice prefix="" info=""] testing different error modes... DB Error: unknown error[db_error: message="db error: unknown error" code=-1 mode=print level=notice prefix="" info=""] User Notice: DB Error: syntax error in PEAR.php on line XXX testing different error serverities... User Notice: DB Error: syntax error in PEAR.php on line XXX User Warning: DB Error: syntax error in PEAR.php on line XXX User Error: DB Error: syntax error in PEAR.php on line XXX DB-1.7.14/tests/db_error2.phpt0000660000175100017510000000624711626162707015611 0ustar danielcdanielc--TEST-- DB::Error 2 --SKIPIF-- --FILE-- toString()) . "\n"; } function myfunc2($obj) { print 'myfunc2 here, obj=' . strtolower($obj->toString()) . "\n"; } class myclass { function myfunc($obj) { print 'myclass::myfunc here, obj=' . strtolower($obj->toString()) . "\n"; } } function test_error_handler($errno, $errmsg, $file, $line, $vars) { if (defined('E_STRICT')) { if ($errno & E_STRICT && (error_reporting() & E_STRICT) != E_STRICT) { // Ignore E_STRICT notices unless they have been turned on return; } } else { define('E_STRICT', 2048); } $errortype = array ( E_ERROR => 'Error', E_WARNING => 'Warning', E_PARSE => 'Parsing Error', E_NOTICE => 'Notice', E_CORE_ERROR => 'Core Error', E_CORE_WARNING => 'Core Warning', E_COMPILE_ERROR => 'Compile Error', E_COMPILE_WARNING => 'Compile Warning', E_USER_ERROR => 'User Error', E_USER_WARNING => 'User Warning', E_USER_NOTICE => 'User Notice', E_STRICT => 'Strict Notice', ); $prefix = $errortype[$errno]; print strtolower("$prefix: $errmsg in " . basename($file) . " on line XXX\n"); } $obj = new myclass; $dbh = DB::factory("mysql"); print "default: "; $e = $dbh->raiseError("return testing error"); print strtolower($e->toString()) . "\n"; print "global default: "; PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, "myfunc2"); $e = $dbh->raiseError("global default test"); $dbh->setErrorHandling(PEAR_ERROR_PRINT); print "mode=print: "; $e = $dbh->raiseError("print testing error"); print "\n"; $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, "myfunc"); print "mode=function callback: "; $e = $dbh->raiseError("function callback testing error"); $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, array($obj, "myfunc")); print "mode=object callback: "; $e = $dbh->raiseError("object callback testing error"); set_error_handler("test_error_handler"); $dbh->setErrorHandling(PEAR_ERROR_TRIGGER); print "mode=trigger: "; $e = $dbh->raiseError("trigger testing error"); ?> --EXPECT-- default: [db_error: message="db error: return testing error" code=-1 mode=return level=notice prefix="" info=" [db error: unknown error]"] global default: myfunc2 here, obj=[db_error: message="db error: global default test" code=-1 mode=callback callback=myfunc2 prefix="" info=" [db error: unknown error]"] mode=print: DB Error: print testing error mode=function callback: myfunc here, obj=[db_error: message="db error: function callback testing error" code=-1 mode=callback callback=myfunc prefix="" info=" [db error: unknown error]"] mode=object callback: myclass::myfunc here, obj=[db_error: message="db error: object callback testing error" code=-1 mode=callback callback=myclass::myfunc prefix="" info=" [db error: unknown error]"] mode=trigger: user notice: db error: trigger testing error in pear.php on line xxx DB-1.7.14/tests/db_factory.phpt0000660000175100017510000000272511626162707016042 0ustar danielcdanielc--TEST-- DB::factory --SKIPIF-- --FILE-- getMessage() . "\n"; } else { print 'object: ' . $obj->toString() . "\n"; } } ?> --GET-- --POST-- --EXPECT-- testing dbase: object: db_dbase: (phptype=dbase, dbsyntax=dbase) testing fbsql: object: db_fbsql: (phptype=fbsql, dbsyntax=fbsql) testing ibase: object: db_ibase: (phptype=ibase, dbsyntax=ibase) testing ifx: object: db_ifx: (phptype=ifx, dbsyntax=ifx) testing msql: object: db_msql: (phptype=msql, dbsyntax=msql) testing mssql: object: db_mssql: (phptype=mssql, dbsyntax=mssql) testing mysql: object: db_mysql: (phptype=mysql, dbsyntax=mysql) testing mysqli: object: db_mysqli: (phptype=mysqli, dbsyntax=mysqli) testing oci8: object: db_oci8: (phptype=oci8, dbsyntax=oci8) testing odbc: object: db_odbc: (phptype=odbc, dbsyntax=sql92) testing pgsql: object: db_pgsql: (phptype=pgsql, dbsyntax=pgsql) testing sqlite: object: db_sqlite: (phptype=sqlite, dbsyntax=sqlite) testing sybase: object: db_sybase: (phptype=sybase, dbsyntax=sybase) DB-1.7.14/tests/db_ismanip.phpt0000660000175100017510000000245111626162707016027 0ustar danielcdanielc--TEST-- DB::isManip --SKIPIF-- --FILE-- --GET-- --POST-- --EXPECT-- testing DB::isManip... SELECT : 0 Select : 0 select : 0 sElECt : 0 SELECT : 0 SELECT : 1 SELECT : 0 UPDATE : 1 DELETE : 1 delete : 1 create : 1 CREATE : 1 "CREATE : 1 GRANT : 1 REVOKE : 1 SHOW : 0 DROP : 1 ALTER : 1 : 0 : 1 DB-1.7.14/tests/db_parsedsn.phpt0000660000175100017510000002445111626162707016212 0ustar danielcdanielc--TEST-- DB::parseDSN --SKIPIF-- --FILE-- 0) { echo "Parsing the original DSN and the DSN generated by getDSNString results in differences:\n"; printf("%-20s%-29s%-29s\n", 'Field', 'Parsed', 'Reparsed'); printf("%-20s%-29s%-29s\n", '-----', '------', '--------'); foreach ($parsed as $key => $value) { printf("%-20s%-29s%-29s\n", $key, $value, $reparsed[$key]); } echo "The reparsed DSN was: ".DB::getDSNString($dsn, false)."\n"; } } function test($dsn) { echo "DSN: $dsn\n"; print_r(DB::parseDSN($dsn)); reparseDSN($dsn); } function testArray($dsn) { echo "DSN: array\n"; print_r(DB::parseDSN($dsn)); reparseDSN($dsn); } print "testing DB::parseDSN...\n\n"; test("mysql"); test("odbc(mssql)"); test('odbc(db2)://user:password@/database'); test('odbc(access):///database'); test('odbc://admin@/datasourceName'); test("mysql://localhost"); test("mysql://remote.host.com/db"); test("oci8://system:manager@"); test("oci8://user:pass@tns-name"); test("odbc(solid)://foo:bar@tcp+localhost+1313"); // deprecated test("pgsql://user@unix+localhost/pear"); // deprecated test("ibase://user%40domain:password@host"); test("ibase://user@domain:pass@word@/database"); // also supported test("ifx://user@domain:pass@word@host.com//usr/db/general.db"); test('ifx://remote.host.com/c:\windows\my.db'); test('oci8://SHOOTOUT:******@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.101.161)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=TIS)))'); // new formats test("odbc(solid)://foo:bar@localhost:1313"); test("pgsql://user@unix()/pear"); test("mysql://user@unix(/path/to/socket)/pear"); test("pgsql://user@tcp()/pear"); test("pgsql://user@tcp(somehost)/pear"); test("pgsql://user:pass@word@tcp(somehost:7777)/pear"); // special backend options test('ibase://user:pass@localhost//var/lib/dbase.dbf?role=foo'); test('dbase://@/?role=foo&dialect=bar'); test('sqlite:////unix/path/to/database?option=value&anotheroption=anothervalue'); test('sqlite:///c:/win/path/to/database?option=value'); // some examples from manual test('mysql://username@hostspec'); test('mysql://hostspec/database'); test('mysql://hostspec'); test('mysql:///database'); // array tests $array = array( 'phptype' => 'mysql', 'hostspec' => 'foobar', ); testArray($array); ?> --GET-- --POST-- --EXPECT-- testing DB::parseDSN... DSN: mysql Array ( [phptype] => mysql [dbsyntax] => mysql [username] => [password] => [protocol] => [hostspec] => [port] => [socket] => [database] => ) DSN: odbc(mssql) Array ( [phptype] => odbc [dbsyntax] => mssql [username] => [password] => [protocol] => [hostspec] => [port] => [socket] => [database] => ) DSN: odbc(db2)://user:password@/database Array ( [phptype] => odbc [dbsyntax] => db2 [username] => user [password] => password [protocol] => tcp [hostspec] => [port] => [socket] => [database] => database ) DSN: odbc(access):///database Array ( [phptype] => odbc [dbsyntax] => access [username] => [password] => [protocol] => tcp [hostspec] => [port] => [socket] => [database] => database ) DSN: odbc://admin@/datasourceName Array ( [phptype] => odbc [dbsyntax] => odbc [username] => admin [password] => [protocol] => tcp [hostspec] => [port] => [socket] => [database] => datasourceName ) DSN: mysql://localhost Array ( [phptype] => mysql [dbsyntax] => mysql [username] => [password] => [protocol] => tcp [hostspec] => localhost [port] => [socket] => [database] => ) DSN: mysql://remote.host.com/db Array ( [phptype] => mysql [dbsyntax] => mysql [username] => [password] => [protocol] => tcp [hostspec] => remote.host.com [port] => [socket] => [database] => db ) DSN: oci8://system:manager@ Array ( [phptype] => oci8 [dbsyntax] => oci8 [username] => system [password] => manager [protocol] => tcp [hostspec] => [port] => [socket] => [database] => ) DSN: oci8://user:pass@tns-name Array ( [phptype] => oci8 [dbsyntax] => oci8 [username] => user [password] => pass [protocol] => tcp [hostspec] => tns-name [port] => [socket] => [database] => ) DSN: odbc(solid)://foo:bar@tcp+localhost+1313 Array ( [phptype] => odbc [dbsyntax] => solid [username] => foo [password] => bar [protocol] => tcp [hostspec] => localhost+1313 [port] => [socket] => [database] => ) DSN: pgsql://user@unix+localhost/pear Array ( [phptype] => pgsql [dbsyntax] => pgsql [username] => user [password] => [protocol] => unix [hostspec] => [port] => [socket] => localhost [database] => pear ) DSN: ibase://user%40domain:password@host Array ( [phptype] => ibase [dbsyntax] => ibase [username] => user@domain [password] => password [protocol] => tcp [hostspec] => host [port] => [socket] => [database] => ) DSN: ibase://user@domain:pass@word@/database Array ( [phptype] => ibase [dbsyntax] => ibase [username] => user@domain [password] => pass@word [protocol] => tcp [hostspec] => [port] => [socket] => [database] => database ) DSN: ifx://user@domain:pass@word@host.com//usr/db/general.db Array ( [phptype] => ifx [dbsyntax] => ifx [username] => user@domain [password] => pass@word [protocol] => tcp [hostspec] => host.com [port] => [socket] => [database] => /usr/db/general.db ) DSN: ifx://remote.host.com/c:\windows\my.db Array ( [phptype] => ifx [dbsyntax] => ifx [username] => [password] => [protocol] => tcp [hostspec] => remote.host.com [port] => [socket] => [database] => c:\windows\my.db ) DSN: oci8://SHOOTOUT:******@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.101.161)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=TIS))) Array ( [phptype] => oci8 [dbsyntax] => oci8 [username] => SHOOTOUT [password] => ****** [protocol] => tcp [hostspec] => (DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.101.161)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=TIS))) [port] => [socket] => [database] => ) DSN: odbc(solid)://foo:bar@localhost:1313 Array ( [phptype] => odbc [dbsyntax] => solid [username] => foo [password] => bar [protocol] => tcp [hostspec] => localhost [port] => 1313 [socket] => [database] => ) DSN: pgsql://user@unix()/pear Array ( [phptype] => pgsql [dbsyntax] => pgsql [username] => user [password] => [protocol] => unix [hostspec] => [port] => [socket] => [database] => pear ) DSN: mysql://user@unix(/path/to/socket)/pear Array ( [phptype] => mysql [dbsyntax] => mysql [username] => user [password] => [protocol] => unix [hostspec] => [port] => [socket] => /path/to/socket [database] => pear ) DSN: pgsql://user@tcp()/pear Array ( [phptype] => pgsql [dbsyntax] => pgsql [username] => user [password] => [protocol] => tcp [hostspec] => [port] => [socket] => [database] => pear ) DSN: pgsql://user@tcp(somehost)/pear Array ( [phptype] => pgsql [dbsyntax] => pgsql [username] => user [password] => [protocol] => tcp [hostspec] => somehost [port] => [socket] => [database] => pear ) DSN: pgsql://user:pass@word@tcp(somehost:7777)/pear Array ( [phptype] => pgsql [dbsyntax] => pgsql [username] => user [password] => pass@word [protocol] => tcp [hostspec] => somehost [port] => 7777 [socket] => [database] => pear ) DSN: ibase://user:pass@localhost//var/lib/dbase.dbf?role=foo Array ( [phptype] => ibase [dbsyntax] => ibase [username] => user [password] => pass [protocol] => tcp [hostspec] => localhost [port] => [socket] => [database] => /var/lib/dbase.dbf [role] => foo ) DSN: dbase://@/?role=foo&dialect=bar Array ( [phptype] => dbase [dbsyntax] => dbase [username] => [password] => [protocol] => tcp [hostspec] => [port] => [socket] => [database] => [role] => foo [dialect] => bar ) DSN: sqlite:////unix/path/to/database?option=value&anotheroption=anothervalue Array ( [phptype] => sqlite [dbsyntax] => sqlite [username] => [password] => [protocol] => tcp [hostspec] => [port] => [socket] => [database] => /unix/path/to/database [option] => value [anotheroption] => anothervalue ) DSN: sqlite:///c:/win/path/to/database?option=value Array ( [phptype] => sqlite [dbsyntax] => sqlite [username] => [password] => [protocol] => tcp [hostspec] => [port] => [socket] => [database] => c:/win/path/to/database [option] => value ) DSN: mysql://username@hostspec Array ( [phptype] => mysql [dbsyntax] => mysql [username] => username [password] => [protocol] => tcp [hostspec] => hostspec [port] => [socket] => [database] => ) DSN: mysql://hostspec/database Array ( [phptype] => mysql [dbsyntax] => mysql [username] => [password] => [protocol] => tcp [hostspec] => hostspec [port] => [socket] => [database] => database ) DSN: mysql://hostspec Array ( [phptype] => mysql [dbsyntax] => mysql [username] => [password] => [protocol] => tcp [hostspec] => hostspec [port] => [socket] => [database] => ) DSN: mysql:///database Array ( [phptype] => mysql [dbsyntax] => mysql [username] => [password] => [protocol] => tcp [hostspec] => [port] => [socket] => [database] => database ) DSN: array Array ( [phptype] => mysql [dbsyntax] => mysql [username] => [password] => [protocol] => [hostspec] => foobar [port] => [socket] => [database] => ) DB-1.7.14/tests/errors.inc0000660000175100017510000003453711626162707015046 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: errors.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Determine if the error from the driver matches the error we expect * * If things are as we expect, print out "matches expected outcome" * * If things go wrong, print "UNEXPECTED OUTCOME" and display the * error's information. * * @param object $e the DB_Error object from the query * @param int $expected_db_code the DB_ERROR* constant to expect * @param boolean $should_be_error does the DBMS consider this an error? * * @return void */ function check_error($e, $expected_db_code, $should_be_error = true) { if ($should_be_error) { if (DB::isError($e)) { if ($e->getCode() == $expected_db_code) { print "matches expected outcome\n"; } else { print "UNEXPECTED OUTCOME...\n"; print ' PEAR::DB errorcode: ' . $e->getCode() . "\n"; print ' ' . $e->getUserInfo() . "\n"; } } else { print "\n UNEXPECTED OUTCOME... expected error but it wasn't\n"; } } else { if (DB::isError($e)) { print "UNEXPECTED OUTCOME... didn't expect error but it was\n"; print ' PEAR::DB errorcode: ' . $e->getCode() . "\n"; print ' ' . $e->getUserInfo() . "\n"; } else { print "matches expected outcome\n"; } } } /** * Local error callback handler * * @param object $o PEAR error object automatically passed to this method * @return void * @see PEAR::setErrorHandling() */ function pe($o) { print "\n---------------\n"; print "Having problems creating a table for testing...\n"; print $o->getDebugInfo() . "\n"; print "---------------\n"; } $dbh->setErrorHandling(PEAR_ERROR_RETURN); print 'DB_ERROR_NOSUCHTABLE for select: '; $res = $dbh->query('SELECT * FROM tableThatsBogus'); check_error($res, DB_ERROR_NOSUCHTABLE); print 'DB_ERROR_NOSUCHTABLE for drop: '; $res = drop_table($dbh, 'tableThatsBogus'); check_error($res, DB_ERROR_NOSUCHTABLE); print 'DB_ERROR_NOT_FOUND for drop index: '; switch ($dbh->phptype . ':' . $dbh->dbsyntax) { case 'fbsql:fbsql': case 'ibase:firebird': case 'ibase:ibase': case 'ifx:ifx': case 'odbc:db2': case 'oci8:oci8': case 'pgsql:pgsql': case 'sqlite:sqlite': $res = $dbh->query('DROP INDEX fakeindex'); break; case 'mssql:mssql': case 'sybase:sybase': $res = $dbh->query('DROP INDEX phptest.fakeindex'); break; case 'msql:msql': $res = $dbh->query('DROP INDEX fakeindex FROM phptest'); break; default: $res = $dbh->query('DROP INDEX fakeindex ON phptest'); } check_error($res, DB_ERROR_NOT_FOUND); print 'DB_ERROR_ALREADY_EXISTS for create table: '; $res = $dbh->query($test_mktable_query); check_error($res, DB_ERROR_ALREADY_EXISTS); print 'DB_ERROR_ALREADY_EXISTS for create index: '; $res = drop_table($dbh, 'a'); $dbh->pushErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $res = $dbh->query('CREATE TABLE a (a INTEGER)'); $dbh->popErrorHandling(); $res = $dbh->query('CREATE INDEX aa_idx ON a (a)'); $res = $dbh->query('CREATE INDEX aa_idx ON a (a)'); switch ($dbh->phptype) { case 'fbsql': // FrontBase doesn't assign a specific code for this yet. check_error($res, DB_ERROR_ALREADY_EXISTS, false); break; default: check_error($res, DB_ERROR_ALREADY_EXISTS); } $res = drop_table($dbh, 'a'); print 'DB_ERROR_CONSTRAINT for primary key insert duplicate: '; $res = drop_table($dbh, 'a'); $dbh->pushErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); switch ($dbh->phptype) { case 'msql': $res = $dbh->query('CREATE TABLE a (a INTEGER NOT NULL)'); $res = $dbh->query('CREATE UNIQUE INDEX apk ON a (a)'); break; default: $res = $dbh->query('CREATE TABLE a (a INTEGER NOT NULL, PRIMARY KEY (a))'); } $dbh->popErrorHandling(); $res = $dbh->query('INSERT INTO a VALUES (1)'); $res = $dbh->query('INSERT INTO a VALUES (1)'); check_error($res, DB_ERROR_CONSTRAINT); print 'DB_ERROR_CONSTRAINT for primary key update duplicate: '; $res = $dbh->query('INSERT INTO a VALUES (2)'); $res = $dbh->query('UPDATE a SET a=1 WHERE a=2'); check_error($res, DB_ERROR_CONSTRAINT); print 'DB_ERROR_CONSTRAINT for unique key insert duplicate: '; $res = drop_table($dbh, 'a'); $dbh->pushErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); switch ($dbh->phptype) { case 'msql': $res = $dbh->query('CREATE TABLE a (a INTEGER NOT NULL)'); $res = $dbh->query('CREATE UNIQUE INDEX auk ON a (a)'); break; default: $res = $dbh->query('CREATE TABLE a (a INTEGER NOT NULL, UNIQUE (a))'); } $dbh->popErrorHandling(); $res = $dbh->query('INSERT INTO a VALUES (1)'); $res = $dbh->query('INSERT INTO a VALUES (1)'); check_error($res, DB_ERROR_CONSTRAINT); print 'DB_ERROR_CONSTRAINT for unique key update duplicate: '; $res = $dbh->query('INSERT INTO a VALUES (2)'); $res = $dbh->query('UPDATE a SET a=1 WHERE a=2'); check_error($res, DB_ERROR_CONSTRAINT); print 'DB_ERROR_CONSTRAINT for foreign key on insert: '; $res = drop_table($dbh, 'b'); $res = drop_table($dbh, 'a'); $dbh->pushErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); switch ($dbh->phptype) { case 'mysql': case 'mysqli': $res = $dbh->query('CREATE TABLE a (a INT NOT NULL, ' . 'PRIMARY KEY (a)) ' . 'TYPE=INNODB'); $res = $dbh->query('CREATE TABLE b (b INT, ' . 'INDEX par_ind (b), ' . 'FOREIGN KEY (b) REFERENCES a (a)) ' . 'TYPE=INNODB'); $dbh->popErrorHandling(); break; case 'msql': // msql does not support foreign keys $res = $dbh->query('CREATE TABLE a (a INTEGER NOT NULL)'); $res = $dbh->query('CREATE UNIQUE INDEX auk ON a (a)'); $dbh->popErrorHandling(); $res = $dbh->query('CREATE TABLE b (b INTEGER REFERENCES a (a))'); if (DB::isError($res)) { print "matches expected outcome\n"; print "DB_ERROR_CONSTRAINT for foreign key on delete: matches expected outcome\n"; } else { print "WOW, it seems mSQL now supports references\n"; print "WOW, it seems mSQL now supports references\n"; } break; default: $res = $dbh->query('CREATE TABLE a (a INTEGER NOT NULL, PRIMARY KEY (a))'); $res = $dbh->query('CREATE TABLE b (b INTEGER REFERENCES a (a))'); $dbh->popErrorHandling(); } if ($dbh->phptype != 'msql') { $res = $dbh->query('INSERT INTO a (a) values (1)'); $res = $dbh->query('INSERT INTO b (b) values (2)'); switch ($dbh->phptype) { case 'sqlite': check_error($res, DB_ERROR_CONSTRAINT, false); break; default: check_error($res, DB_ERROR_CONSTRAINT); } print 'DB_ERROR_CONSTRAINT for foreign key on delete: '; $res = $dbh->query('INSERT INTO b (b) values (1)'); $res = $dbh->query('DELETE FROM a WHERE a = 1'); switch ($dbh->phptype) { case 'sqlite': check_error($res, DB_ERROR_CONSTRAINT, false); break; default: check_error($res, DB_ERROR_CONSTRAINT); } } print 'DB_ERROR_CONSTRAINT_NOT_NULL on insert: '; $res = drop_table($dbh, 'peartestnull'); $dbh->pushErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $res = $dbh->query('CREATE TABLE peartestnull (a CHAR(3) NOT NULL)'); $dbh->popErrorHandling(); $res = $dbh->query('INSERT INTO peartestnull VALUES (NULL)'); check_error($res, DB_ERROR_CONSTRAINT_NOT_NULL); print 'DB_ERROR_CONSTRAINT_NOT_NULL on update: '; $res = $dbh->query("INSERT INTO peartestnull VALUES ('one')"); $res = $dbh->query("UPDATE peartestnull SET a = NULL WHERE a = 'one'"); switch ($dbh->phptype) { case 'mysql': case 'mysqli': check_error($res, DB_ERROR_CONSTRAINT_NOT_NULL, false); break; default: check_error($res, DB_ERROR_CONSTRAINT_NOT_NULL); } print 'DB_ERROR_NOSUCHFIELD joining ON bogus column: '; $res = $dbh->query('SELECT * FROM phptest JOIN a ON (phptest.a = a.b)'); switch ($dbh->phptype . ':' . $dbh->dbsyntax) { case 'msql:msql': case 'odbc:access': check_error($res, DB_ERROR_SYNTAX); break; default: check_error($res, DB_ERROR_NOSUCHFIELD); } print 'DB_ERROR_NOSUCHFIELD joining USING bogus column: '; $res = $dbh->query('SELECT * FROM phptest JOIN a USING (b)'); switch ($dbh->phptype . ':' . $dbh->dbsyntax) { case 'ibase:firebird': /* FirebirdSQL 2 returns -902 (feature is not supported) for this test * rather than a syntax error. For now, we'll test for both. */ if ($res->getCode() == DB_ERROR_SYNTAX) check_error($res, DB_ERROR_SYNTAX); else check_error($res, DB_ERROR_NOT_CAPABLE); break; case 'ibase:ibase': case 'ifx:ifx': case 'msql:msql': case 'odbc:access': case 'odbc:db2': case 'sybase:sybase': check_error($res, DB_ERROR_SYNTAX); break; default: check_error($res, DB_ERROR_NOSUCHFIELD); } print 'DB_ERROR_DIVZERO: '; // Interbase detects the error on fetching $res = $dbh->getAll('SELECT 0/0 FROM phptest'); switch ($dbh->phptype) { case 'odbc': switch ($dbh->dbsyntax) { case 'access': check_error($res, DB_ERROR_DIVZERO, false); break; case 'db2': check_error($res, DB_ERROR_DIVZERO); break; } break; case 'msql': check_error($res, DB_ERROR_SYNTAX); break; case 'fbsql': case 'ibase': case 'mssql': case 'mysql': case 'mysqli': case 'sqlite': /* Not all databases detect division by zero errors. We call these * databases "broken". */ check_error($res, null, false); break; default: check_error($res, DB_ERROR_DIVZERO); } print 'DB_ERROR_INVALID_NUMBER putting chars in INT column: '; $res = $dbh->query("UPDATE phptest SET a = 'abc' WHERE a = 42"); switch ($dbh->phptype) { case 'mysql': case 'mysqli': case 'sqlite': check_error($res, DB_ERROR_INVALID_NUMBER, false); break; default: check_error($res, DB_ERROR_INVALID_NUMBER); } print 'DB_ERROR_INVALID_NUMBER putting float in INT column: '; $res = $dbh->query("UPDATE phptest SET a = 8.9 WHERE a = 42"); switch ($dbh->phptype) { case 'fbsql': case 'ibase': case 'ifx': case 'mssql': case 'mysql': case 'mysqli': case 'oci8': case 'odbc': case 'pgsql': case 'sqlite': check_error($res, DB_ERROR_INVALID_NUMBER, false); break; default: check_error($res, DB_ERROR_INVALID_NUMBER); } print 'DB_ERROR_INVALID_NUMBER putting excessive int in INT column: '; $res = $dbh->query("UPDATE phptest SET a = 18446744073709551616 WHERE a = 42"); switch ($dbh->phptype . ':' . $dbh->dbsyntax) { case 'ibase:ibase': case 'ibase:firebird': check_error($res, DB_ERROR_SYNTAX); break; case 'fbsql:fbsql': case 'ifx:ifx': case 'msql:msql': case 'mssql:mssql': case 'mysql:mysql': case 'mysqli:mysqli': case 'oci8:oci8': case 'odbc:access': case 'sqlite:sqlite': check_error($res, DB_ERROR_INVALID_NUMBER, false); break; default: check_error($res, DB_ERROR_INVALID_NUMBER); } print 'DB_ERROR_INVALID_NUMBER putting int in CHAR column: '; $res = $dbh->query("UPDATE phptest SET b = 8 WHERE a = 42"); switch ($dbh->phptype . ':' . $dbh->dbsyntax) { case 'ibase:ibase': case 'ibase:firebird': case 'ifx:ifx': case 'mssql:mssql': case 'mysql:mysql': case 'mysqli:mysqli': case 'oci8:oci8': case 'odbc:access': case 'pgsql:pgsql': case 'sqlite:sqlite': check_error($res, DB_ERROR_INVALID_NUMBER, false); break; default: check_error($res, DB_ERROR_INVALID_NUMBER); } print 'DB_ERROR_NOSUCHFIELD: '; $res = $dbh->query('SELECT e FROM phptest'); check_error($res, DB_ERROR_NOSUCHFIELD); print 'DB_ERROR_SYNTAX: '; $res = $dbh->query('CREATE'); check_error($res, DB_ERROR_SYNTAX); print 'DB_ERROR_VALUE_COUNT_ON_ROW: '; $res = $dbh->query('INSERT INTO phptest (a) VALUES (678, 2)'); switch ($dbh->phptype) { case 'msql': check_error($res, DB_ERROR_VALUE_COUNT_ON_ROW, false); break; default: check_error($res, DB_ERROR_VALUE_COUNT_ON_ROW); } print 'DB_ERROR_INVALID on CHAR column data too long: '; $res = $dbh->query("INSERT INTO phptest (b) VALUES ('123456789.123456789.123456789.123456789.1')"); switch ($dbh->phptype . ':' . $dbh->dbsyntax) { case 'ifx:ifx': case 'msql:msql': case 'mssql:mssql': case 'mysql:mysql': case 'mysqli:mysqli': case 'odbc:access': case 'sqlite:sqlite': case 'sybase:sybase': check_error($res, DB_ERROR_INVALID, false); break; case 'fbsql:fbsql': check_error($res, DB_ERROR_TRUNCATED); break; default: check_error($res, DB_ERROR_INVALID); } print 'DB_ERROR_INVALID on VARCHAR column data too long: '; $res = $dbh->query("INSERT INTO phptest (d) VALUES ('123456789.123456789.1')"); switch ($dbh->phptype . ':' . $dbh->dbsyntax) { case 'ifx:ifx': case 'msql:msql': case 'mssql:mssql': case 'mysql:mysql': case 'mysqli:mysqli': case 'odbc:access': case 'sqlite:sqlite': case 'sybase:sybase': check_error($res, DB_ERROR_INVALID, false); break; case 'fbsql:fbsql': check_error($res, DB_ERROR_TRUNCATED); break; default: check_error($res, DB_ERROR_INVALID); } drop_table($dbh, 'phptest'); drop_table($dbh, 'b'); drop_table($dbh, 'a'); drop_table($dbh, 'peartestnull'); DB-1.7.14/tests/fetchmodes.inc0000660000175100017510000001464411626162707015650 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: fetchmodes.inc 242790 2007-09-21 15:14:26Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Local error callback handler * * Drops the phptest table, prints out an error message and kills the * process. * * @param object $o PEAR error object automatically passed to this method * @return void * @see PEAR::setErrorHandling() */ function pe($o) { global $dbh; $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); die($o->toString()); } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $dbh->setOption('autofree', true); $dbh->query("INSERT INTO phptest VALUES (1, 'one', 'One', '2001-02-16')"); $dbh->query("INSERT INTO phptest VALUES (2, 'two', 'Two', '2001-02-15')"); $dbh->query("INSERT INTO phptest VALUES (3, 'three', 'Three', '2001-02-14')"); print "testing fetchrow:\n"; $sth = $dbh->query("SELECT * FROM phptest"); for ($i = 1; $i <= 5; $i++) { print "row $i: "; $row = $sth->fetchRow(); if (DB::isError($row)) { print $row->toString() . "\n"; continue; } if (is_array($row)) { print implode(', ', $row) . "\n"; } else { var_dump($row); } } $sth->free(); // keep fbsql happy. $dbh->query('DELETE FROM phptest WHERE a <> 42'); print "testing fetchmodes: fetchrow default default, portability mode DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM\n"; $dbh->setOption('portability', DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM); $sth = $dbh->query("SELECT * FROM phptest"); $row = $sth->fetchRow(); print implode(" ", array_keys($row))."\n"; $actual = implode(' ', array_values($row)); switch ($dbh->phptype) { case 'fbsql': case 'msql': case 'mysql': case 'mysqli': case 'sqlite': $expected = '42 bing This is a test 1999-11-21'; break; case 'ifx': $expected = '42 bing This is a test 1999-11-21 '; break; default: $expected = '42 bing This is a test 1999-11-21'; } if ($actual == $expected) { echo "output matched expected format\n"; } else { echo "DIDN'T MATCH! Expected output: '$expected'. Actual output: '$actual'.\n"; } $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchinto default default\n"; $dbh->setOption('portability', DB_PORTABILITY_ALL); $sth = $dbh->query("SELECT * FROM phptest"); $row = array(); $sth->fetchInto($row); print implode(" ", array_keys($row))."\n"; print implode(' ', array_values($row))."\n"; $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchrow ordered default\n"; $dbh->setFetchMode(DB_FETCHMODE_ORDERED); $sth = $dbh->query("SELECT * FROM phptest"); $row = $sth->fetchRow(); print implode(" ", array_keys($row))."\n"; $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchrow assoc default\n"; $dbh->setFetchMode(DB_FETCHMODE_ASSOC); $sth = $dbh->query("SELECT * FROM phptest"); $row = $sth->fetchRow(); print implode(" ", array_keys($row))."\n"; $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchrow ordered default with assoc specified\n"; $dbh->setFetchMode(DB_FETCHMODE_ORDERED); $sth = $dbh->query("SELECT * FROM phptest"); $row = $sth->fetchRow(DB_FETCHMODE_ASSOC); print implode(" ", array_keys($row))."\n"; $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchrow assoc default with ordered specified\n"; $dbh->setFetchMode(DB_FETCHMODE_ASSOC); $sth = $dbh->query("SELECT * FROM phptest"); $row = $sth->fetchRow(DB_FETCHMODE_ORDERED); print implode(" ", array_keys($row))."\n"; $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchinto ordered default\n"; $dbh->setFetchMode(DB_FETCHMODE_ORDERED); $sth = $dbh->query("SELECT * FROM phptest"); $row = array(); $sth->fetchInto($row); print implode(" ", array_keys($row))."\n"; $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchinto assoc default\n"; $dbh->setFetchMode(DB_FETCHMODE_ASSOC); $sth = $dbh->query("SELECT * FROM phptest"); $row = array(); $sth->fetchInto($row); print implode(" ", array_keys($row))."\n"; $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchinto ordered default with assoc specified\n"; $dbh->setFetchMode(DB_FETCHMODE_ORDERED); $sth = $dbh->query("SELECT * FROM phptest"); $row = array(); $sth->fetchInto($row, DB_FETCHMODE_ASSOC); print implode(" ", array_keys($row))."\n"; $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchinto assoc default with ordered specified\n"; $dbh->setFetchMode(DB_FETCHMODE_ASSOC); $sth = $dbh->query("SELECT * FROM phptest"); $row = array(); $sth->fetchInto($row, DB_FETCHMODE_ORDERED); print implode(" ", array_keys($row))."\n"; $sth->free(); // keep fbsql happy. print "testing fetchmodes: fetchrow assoc quoted identifiers\n"; if ($dbh->phptype == 'msql' || $dbh->phptype == 'ibase' || $dbh->phptype == 'oci8') { // Some databases don't support quoted identifiers. Fake the output. echo "a b cc d\n"; } else { $dbh->setFetchMode(DB_FETCHMODE_ASSOC); $sql = sprintf('SELECT %s, %s, %s, %s FROM %s', $dbh->quoteIdentifier('a'), $dbh->quoteIdentifier('b'), $dbh->quoteIdentifier('cc'), $dbh->quoteIdentifier('d'), $dbh->quoteIdentifier('phptest')); $sth = $dbh->query($sql); $row = $sth->fetchRow(); print implode(" ", array_keys($row))."\n"; $sth->free(); // keep fbsql happy. // keep ibase happy: can't drop tbl that has results open against it. } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); DB-1.7.14/tests/fetchmode_object.inc0000660000175100017510000000622411626162707017006 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: fetchmode_object.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ error_reporting(E_ALL); /** * Local error callback handler * * Drops the phptest table, prints out an error message and kills the * process. * * @param object $o PEAR error object automatically passed to this method * @return void * @see PEAR::setErrorHandling() */ function pe($o) { global $dbh; $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); die($o->toString()); } /** * Print out the object */ function print_obj(&$obj) { if (!is_object($obj)) { echo "ERROR: no object!\n"; } else { echo strtolower(get_class($obj)) . ' -> ' . implode(' ', array_keys((array)$obj)) . "\n"; } } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); echo "--- fetch with param DB_FETCHMODE_OBJECT ---\n"; $sth = $dbh->query("SELECT * FROM phptest"); $row = $sth->fetchRow(DB_FETCHMODE_OBJECT); print_obj($row); $sth->free(); // keep fbsql happy. $sth = $dbh->query("SELECT * FROM phptest"); $sth->fetchInto($row, DB_FETCHMODE_OBJECT); print_obj($row); $sth->free(); // keep fbsql happy. echo "--- fetch with default fetchmode DB_FETCHMODE_OBJECT ---\n"; $dbh->setFetchMode(DB_FETCHMODE_OBJECT); $sth = $dbh->query("SELECT * FROM phptest"); $row = $sth->fetchRow(); print_obj($row); $sth->free(); // keep fbsql happy. $sth = $dbh->query("SELECT * FROM phptest"); $sth->fetchInto($row); print_obj($row); $sth->free(); // keep fbsql happy. echo "--- fetch with default fetchmode DB_FETCHMODE_OBJECT and class DB_row ---\n"; $dbh->setFetchMode(DB_FETCHMODE_OBJECT, 'DB_row'); $sth = $dbh->query("SELECT * FROM phptest"); $row = $sth->fetchRow(); print_obj($row); $sth->free(); // keep fbsql happy. $sth = $dbh->query("SELECT * FROM phptest"); $sth->fetchInto($row); print_obj($row); $sth->free(); // keep fbsql happy. echo "--- fetch with default fetchmode DB_FETCHMODE_OBJECT with no class then DB_row ---\n"; $dbh->setFetchMode(DB_FETCHMODE_OBJECT); $sth = $dbh->query('SELECT * FROM phptest'); $row = $sth->fetchRow(); print_obj($row); $sth->free(); // keep fbsql happy. $dbh->setFetchMode(DB_FETCHMODE_OBJECT, 'DB_row'); $sth = $dbh->query('SELECT * FROM phptest'); $row = $sth->fetchRow(); print_obj($row); $sth->free(); // keep fbsql happy. // keep ibase happy: can't drop tbl that has results open against it. $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); DB-1.7.14/tests/include.inc0000660000175100017510000000403111626162707015137 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: include.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ error_reporting(E_ALL); if (!defined('PATH_SEPARATOR')) { if (stristr(PHP_OS, 'WIN')) { /** * Define the path separator for windows */ define('PATH_SEPARATOR', ';'); } else { /** * Define the path separator for other systems */ define('PATH_SEPARATOR', ':'); } } /* * If the path to your PEAR installation is found in the left hand * portion of the if() expression below, that means this file has * come from the PEAR installer. Therefore, let's use the * installed version of DB, which should be found via the * computer's default include_path. Add '.' to the include_path * to ensure '.' is in there. * * If the path has not been substituted in the if() expression, * this file has likely come from a CVS checkout or a .tar file. * Therefore, we'll assume the tests should use the version of * DB that has come from there as well. */ if ('@include_path@' != '@'.'include_path'.'@') { ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . '.' ); } else { ini_set('include_path', realpath(dirname(__FILE__) . '/..') . PATH_SEPARATOR . '.' . PATH_SEPARATOR . ini_get('include_path') ); } DB-1.7.14/tests/limit.inc0000660000175100017510000000604311626162707014637 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: limit.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ error_reporting(E_ALL); /** * Local error callback handler * * Drops the phptest table, prints out an error message and kills the * process. * * @param object $o PEAR error object automatically passed to this method * @return void * @see PEAR::setErrorHandling() */ function pe($o) { global $dbh; $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); die($o->toString()); } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'php_limit'); $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $dbh->query('CREATE TABLE php_limit (a CHAR(20))'); $from = 0; $count = 10; $numrows = 30; for ($i=0; $i<=$numrows+2; $i++) { $dbh->query("INSERT INTO php_limit VALUES('result $i')"); } for ($i = 0; $i <= 3; $i++) { $from = 10 * $i; $res = $dbh->limitQuery("select * from php_limit", $from, $count); echo "======= From: $from || Number of rows to fetch: $count =======\n"; while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) { echo $res->getRowCounter() . '.- ' . $row['a'] . "\n"; } $count = $res->numRows(); echo "Row count for limited result: $count\n"; $res->free(); // keep fbsql happy. } $from = 11; $count = 3; echo "======= Passing \$params || From: $from || Number of rows to fetch: $count =======\n"; $res = $dbh->limitQuery('SELECT * FROM php_limit WHERE a < ?', $from, $count, array('result 99')); while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) { echo $res->getRowCounter() . '.- ' . $row['a'] . "\n"; } $res->free(); // keep fbsql happy. // keep ibase happy: can't drop tbl that has results open against it. // Regression test for bug #7502. $from = 0; $count = 3; $iter = 10; echo "======= From: $from || Number of rows to fetch: $count || Iterations: $iter =======\n"; $res = $dbh->limitQuery("select * from php_limit", $from, $count); if (!in_array($dbh->phptype, array('ibase', 'oci8'))) { for ($i = 0; $i < $iter; ++$i) { if (is_null($row = $res->fetchRow(DB_FETCHMODE_ASSOC, 1))) { echo "Error in iteration $i: {$row['a']}\n"; } } } $count = $res->numRows(); echo "Row count for limited result: $count\n"; $res->free(); // keep fbsql happy. $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'php_limit'); DB-1.7.14/tests/numcols.inc0000660000175100017510000000351211626162707015177 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: numcols.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Local error callback handler * * Drops the phptest table, prints out an error message and kills the * process. * * @param object $o PEAR error object automatically passed to this method * @return void * @see PEAR::setErrorHandling() */ function pe($o) { global $dbh; $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); die($o->toString()); } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $sth = $dbh->query("SELECT a FROM phptest"); printf("%d\n", $sth->numCols()); $sth = $dbh->query("SELECT a,b FROM phptest"); printf("%d\n", $sth->numCols()); $sth = $dbh->query("SELECT a,b,cc FROM phptest"); printf("%d\n", $sth->numCols()); $sth = $dbh->query("SELECT * FROM phptest"); printf("%d\n", $sth->numCols()); switch ($dbh->phptype) { case 'ibase': /* * Interbase doesn't allow dropping tables that have result * sets still open. */ $dbh->freeResult($sth->result); break; } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); DB-1.7.14/tests/numrows.inc0000660000175100017510000000615411626162707015236 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: numrows.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Local error callback handler * * Drops the phptest table, prints out an error message and kills the * process. * * @param object $o PEAR error object automatically passed to this method * @return void * @see PEAR::setErrorHandling() */ function pe($o) { global $dbh, $res; $dbh->setErrorHandling(PEAR_ERROR_RETURN); $res->free(); drop_table($dbh, 'phptest'); print "\n------------\n"; if ($o->getCode() == DB_ERROR_UNSUPPORTED) { print "This DBMS does not support numRows()."; } elseif ($o->getCode() == DB_ERROR_MISMATCH) { print "Mismatch between the number of placeholders and parameters.\n"; foreach ($o->backtrace as $item => $detail) { if ($detail['function'] == 'query') { echo 'QUERY: ' . $detail['args'][0] . "\n"; echo "PARAMETERS:\n"; print_r($detail['args'][1]); } } } else { print $o->getDebugInfo() . "\n"; } exit; } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $res = $dbh->query("SELECT a FROM phptest"); if (!DB::isError($rows = $res->numRows())) { print "(want 1) got $rows from first\n"; } else { print "\n"; } for ($i = 0; $i < 5; $i++) { $dbh->query("INSERT INTO phptest (a) VALUES ($i)"); $res = $dbh->query("SELECT a FROM phptest"); if (!DB::isError($rows = $res->numRows())) { print '(want ' . ($i + 2) . ") got $rows from $i\n"; } else { print "\n"; } } $res = $dbh->query('SELECT a FROM phptest WHERE a > ?', array(0)); if (!DB::isError($rows = $res->numRows())) { print "(want 5) got $rows from > 0 (passing params to query)\n"; } else { print "\n"; } $sth = $dbh->prepare('SELECT a FROM phptest WHERE a < ?'); $res = $dbh->execute($sth, array(4)); if (!DB::isError($rows = $res->numRows())) { print "(want 4) got $rows from < 4 (doing prepare/execute)\n"; } else { print "\n"; } $dbh->query("DELETE FROM phptest WHERE a < 4"); $res = $dbh->query("SELECT a FROM phptest"); if (!DB::isError($rows = $res->numRows())) { print "(want 2) got $rows from 5 and 6 not deleted\n"; } else { print "\n"; } $res = $dbh->query("SELECT a FROM phptest where a < 0"); if (!DB::isError($rows = $res->numRows())) { print "(want 0) got $rows from < 0\n"; } else { print "\n"; } drop_table($dbh, 'phptest'); DB-1.7.14/tests/prepexe.inc0000660000175100017510000001472411626162707015176 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: prepexe.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ $tmpfile = tempnam("/tmp", "phptmp"); register_shutdown_function("my_shutdown"); $fp = fopen($tmpfile, "w"); $filedata = "opaque placeholder's test"; fwrite($fp, $filedata); fclose($fp); /** * Local error callback handler * * Prints out an error message and kills the process. * * @param object $o PEAR error object automatically passed to this method * @return void * @see PEAR::setErrorHandling() */ function pe($o) { print "\n" . $o->toString(); exit; } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); // 1) Multiple prepare/exec INSERT queries echo "------------1------------\n"; $sth1 = $dbh->prepare("INSERT INTO phptest (a, b) VALUES (?, 'a')"); $sth2 = $dbh->prepare("INSERT INTO phptest (a,b) VALUES (!,?)"); $sth3 = $dbh->prepare("INSERT INTO phptest (a,b,cc) VALUES (?,!,&)"); $sth4 = $dbh->prepare("INSERT INTO phptest (a, b) VALUES (72, 'direct')"); print "sth1,sth2,sth3,sth4 created\n"; print 'sth1: ? as param, passing as array... '; if (($res = $dbh->execute($sth1, array(72))) === DB_OK) { print "sth1 executed\n"; } else { print "sth1 failed\n"; } print 'sth2: ! and ? as params, passing as array... '; if (($res = $dbh->execute($sth2, array(72, "that's right"))) === DB_OK) { print "sth2 executed\n"; } else { print "sth2 failed\n"; } print 'sth3: ?, ! and & as params, passing as array... '; switch ($dbh->phptype) { case 'msql': $res = $dbh->execute($sth3, array(72, "'it\\'s good'", $tmpfile)); break; default: $res = $dbh->execute($sth3, array(72, "'it''s good'", $tmpfile)); } if ($res === DB_OK) { print "sth3 executed\n"; } else { print "sth3 failed\n"; } print 'sth4: no params... '; if (($res = $dbh->execute($sth4)) === DB_OK) { print "sth4 executed\n"; } else { print "sth4 failed\n"; } print_results(); // 2) One prepared, multiple time executed echo "\n------------2------------\n"; $dbh->query('DELETE FROM phptest'); $sth = $dbh->prepare('INSERT INTO phptest (a, b, cc, d) VALUES (?, ?, &, ?)'); $data = array( 0 => array(72, 'set1', $tmpfile, '1234-56-78'), 1 => array(72, 'set2', $tmpfile, null), 2 => array(72, 'set3', $tmpfile, null) ); $res = $dbh->executeMultiple($sth, $data); print_results(); // 3) freePrepared() test echo "\n------------3------------\n"; if ($dbh->freePrepared($sth)) { echo 'TRUE'; } else { echo 'FALSE'; } echo "\n"; if ($dbh->freePrepared(666)) { echo 'TRUE'; } else { echo 'FALSE'; } echo "\n"; // 4) SELECTs tests echo "\n------------4------------\n"; $sth1 = $dbh->prepare("SELECT * FROM phptest WHERE a = ? ORDER BY b"); print_4($sth1, 72); print_4($sth1, 71); $sth2 = $dbh->prepare("SELECT * FROM phptest WHERE d = ? ORDER BY b"); print_4($sth2, '1234-56-78'); $sth3 = $dbh->prepare("SELECT * FROM phptest WHERE cc = & ORDER BY b"); print_4($sth3, $tmpfile); // 5) ASSOCIATIVE ARRAY queries echo "\n------------5------------\n"; $sth5 = $dbh->prepare('INSERT INTO phptest (a, b, d) VALUES (?, ?, ?)'); $array = array( 'foo' => 11, 'bar' => 'three', 'baz' => null, ); $res = $dbh->execute($sth5, $array); print 'insert: ' . ($res === DB_OK ? 'okay' : 'error') . "\n"; $sth6 = $dbh->prepare('SELECT a, b, d FROM phptest WHERE a = ?'); $res = $dbh->execute($sth6, array(11)); $row = $res->fetchRow(DB_FETCHMODE_ASSOC); print "a = {$row['a']}, b = {$row['b']}, d = "; if ($dbh->phptype == 'msql') { if (array_key_exists('d', $row)) { $type = gettype($row['d']); if ($type == 'NULL' || $row['d'] == '') { print "got expected outcome\n"; } else { $type = gettype($row['d']); print "UN-expected outcome: $type\n"; } } else { // http://bugs.php.net/?id=31960 print "Prior to PHP 4.3.11 or 5.0.4, PHP's msql extension silently" . " dropped columns with null values. You need to upgrade.\n"; } } else { $type = gettype($row['d']); if ($type == 'string') { print "got expected outcome\n"; } else { print "UN-expected outcome: $type\n"; } } /** * Automatically free the prepared statements and results when the script * terminates * * @return void */ function my_shutdown() { global $tmpfile, $dbh, $sth1, $sth2, $sth3, $sth4, $sth5, $sth6, $res; switch ($dbh->phptype) { case 'ibase': /* * Interbase doesn't allow dropping tables that have result * sets still open. */ $dbh->freePrepared($sth1); $dbh->freePrepared($sth2); $dbh->freePrepared($sth3); $dbh->freePrepared($sth4); $dbh->freePrepared($sth5); $dbh->freePrepared($sth6); $dbh->freeResult($res->result); break; } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); unlink($tmpfile); } /** * Print out the data in test table * * @return void */ function print_results() { global $dbh; print "results:\n"; $res = $dbh->query("SELECT * FROM phptest WHERE a = 72 ORDER BY b"); $i = 0; while ($row = $res->fetchRow(DB_FETCHMODE_ORDERED)) { print '|' . implode(" - ", $row) . "|\n"; $i++; } if (!$i) { print "The records were not found. Did they get inserted?\n"; } } /** * Execute the prepared statement and print out the data in the result * * @param resource $sth the statement handle to process * @param string|array $bind the data that will replace the placeholders * * @return void */ function print_4($sth, $bind) { global $dbh; $res = $dbh->execute($sth, $bind); while ($row = $res->fetchRow(DB_FETCHMODE_ORDERED)) { print '|' . implode(" - ", $row) . "|\n"; } echo "~~\n"; } DB-1.7.14/tests/run.cvs0000770000175100017510000000151711626162707014352 0ustar danielcdanielc#! /bin/sh # vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv # PEAR DB TEST STARTER # # To run all tests: ./run # To run one test: ./run # Example: ./run db_parsedsn.phpt # # Before running the tests you must adjust the # following three variables: # The path and name of your run-tests.php file: DB_TEST_RUN_TESTS=c:/progra~1/php/run-tests.php # The path and name of your PHP CLI executable # (example c:/progra~1/php.exe): TEST_PHP_EXECUTABLE=c:/progra~1/php.exe # The full path to the present directory # (not using $PWD due to Cygwin): DB_TEST_DIR=d:/peartest/pear/DB/tests # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ export TEST_PHP_EXECUTABLE if [ $# -gt 0 ] then test=$1 else test=*.phpt fi $TEST_PHP_EXECUTABLE $DB_TEST_RUN_TESTS $DB_TEST_DIR/${test} DB-1.7.14/tests/sequences.inc0000660000175100017510000000755611626162707015526 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: sequences.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Local error handler */ function error_handler(&$obj) { print "sequences.inc error_handler:\n "; print $obj->getDebugInfo() . "\n\n"; } ob_implicit_flush(true); $drop = $dbh->dropSequence('test'); if (DB::isError($drop) && $drop->getCode() != DB_ERROR_NOSUCHTABLE) { print "Could not drop sequence...\n"; print $drop->getDebugInfo() . "\n\n"; if ($dbh->phptype == 'ibase' && $drop->getCode() == DB_ERROR_ACCESS_VIOLATION) { print "Use this query to provide the permissions needed:\n"; print ' grant all on RDB$GENERATORS to '; } exit; } // 1) test that sequences are not created if "ondemand" is false $e = $dbh->nextId("test", false); if (DB::isError($e) && $e->getCode() == DB_ERROR_NOSUCHTABLE) { print "an error is the proper response here\n"; } else { if (DB::isError($e)) { if ($dbh->phptype == 'ibase' && $e->getCode() == DB_ERROR_SYNTAX) { print "an error is the proper response here\n"; } else { print "test 1) we expected to get back 'DB Error: no such table'.\n"; print "Here is the error we got:\n"; print 'Code: ' . $e->getCode() . "\n"; print 'Message: ' . $e->getMessage() . "\n"; print 'Debug: ' . $e->getDebugInfo() . "\n\n"; } } else { print "test 1) we expected to get back 'DB Error: no such table'.\n"; print "But an error wasn't generated\n\n"; } } // 2) test that the sequence is not created but the error is // handled by the class error handler $dbh->setErrorHandling(PEAR_ERROR_PRINT, "an error cought by the error handler is good\n"); $e = $dbh->nextId("test", false); if (!DB::isError($e)) { print "test 2) failed!\n"; } $dbh->_default_error_mode = null; // 3) test that sequences are created if "ondemand" is true, and that // two successive nextIds return adjacent values $a = $dbh->nextId("test"); $b = $dbh->nextId("test"); if (DB::isError($a)) { print 'a: ' . $a->getDebugInfo() . "\n\n"; } else { print "a=$a\n"; } if (DB::isError($b)) { print 'b: ' . $b->getDebugInfo() . "\n\n"; } else { print "b=$b\n"; } if (!DB::isError($a) && !DB::isError($b)) { print 'b-a=' . ($b-$a) . "\n"; } // 4) test that the user-specified error handler is really disabled // during nextId, with per-object handler as well as global handler $dbh->dropSequence("test"); $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'error_handler'); $c = $dbh->nextId("test"); if (!DB::isError($c)) { print "c=$c\n"; } $dbh->dropSequence("test"); $dbh->_default_error_mode = null; $d = $dbh->nextId("test"); if (!DB::isError($d)) { print "d=$d\n"; } // 5) test that the sequence is handled right when the table is empty // Backend with real sequences may don't like that PEAR::pushErrorHandling(PEAR_ERROR_RETURN); $dbh->query('DELETE FROM test_seq'); PEAR::popErrorHandling(); $e = $dbh->nextID('test'); if (DB::isError($d)) { print 'e: ' . $d->getDebugInfo() . "\n\n"; } else { print "e=$d\n"; } // final clean-up $dbh->dropSequence("test"); DB-1.7.14/tests/simplequery.inc0000660000175100017510000000362311626162707016101 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: simplequery.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ /** * Local error callback handler * * Drops the phptest table, prints out an error message and kills the * process. * * @param object $o PEAR error object automatically passed to this method * @return void * @see PEAR::setErrorHandling() */ function pe($o) { global $dbh; $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); die($o->toString()); } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'pe'); $sth = $dbh->simpleQuery("SELECT * FROM phptest"); switch ($dbh->phptype) { case 'mysqli': if (is_a($sth, 'mysqli_result')) { print "passed\n"; } else { print "PROBLEM\n"; } break; default: if (gettype($sth) == 'resource') { print "passed\n"; } else { print "PROBLEM\n"; } } switch ($dbh->phptype) { case 'ibase': /* * Interbase doesn't allow dropping tables that have result * sets still open. */ $dbh->freeResult($sth); break; } $dbh->setErrorHandling(PEAR_ERROR_RETURN); drop_table($dbh, 'phptest'); DB-1.7.14/tests/skipif.inc0000770000175100017510000000170011626162707015003 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: skipif.inc 284375 2009-07-19 16:26:16Z danielc $ * @link http://pear.php.net/package/DB */ /** * Set up the include_path, error_reporting and PATH_SEPARATOR */ require_once dirname(__FILE__) . '/include.inc'; if (!include_once 'DB.php') { print 'skip could not find DB.php'; } DB-1.7.14/tests/transactions.inc0000660000175100017510000000564311626162707016236 0ustar danielcdanielc * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version $Id: transactions.inc 239211 2007-07-06 05:19:21Z aharvey $ * @link http://pear.php.net/package/DB */ // Testing here due to skip not working currently in head if (!$dbh->features['transactions']) { die('this driver does not support transactions'); } // View the table from a separate connection so we don't disturb // the transaction. $dbh2 = DB::connect($dbh->dsn); function error_handler(&$obj) { print "\n" . $obj->getDebugInfo() . "\n"; } function dumptable($expected) { global $dbh, $dbh2; print implode(' ', $dbh->getCol('SELECT b FROM phptest')); if (isset($dbh->transaction_opcount)) { if ($expected == $dbh->transaction_opcount) { print ". ops=ok\n"; } else { print ". ops=$dbh->transaction_opcount\n"; } } else { print ". ops=ok\n"; } } $dbh->setErrorHandling(PEAR_ERROR_CALLBACK, 'error_handler'); $dbh->autoCommit(true); $dbh->query("INSERT INTO phptest VALUES(1, 'one', 'One', '2001-02-19')"); print '1) after autocommit: '; dumptable(0); $dbh->autoCommit(false); $dbh->query("INSERT INTO phptest VALUES(2, 'two', 'Two', '2001-02-20')"); $dbh->query("INSERT INTO phptest VALUES(3, 'three', 'Three', '2001-02-21')"); print '2) before commit: '; dumptable(2); $dbh->commit(); print '3) after commit: '; dumptable(0); $dbh->query("INSERT INTO phptest VALUES(4, 'four', 'Four', '2001-02-22')"); $dbh->query("INSERT INTO phptest VALUES(5, 'five', 'Five', '2001-02-23')"); print '4) before rollback: '; dumptable(2); $dbh->rollback(); print '5) after rollback: '; dumptable(0); $dbh->rollback(); $dbh->autoCommit(true); $dbh->query("INSERT INTO phptest VALUES(6, 'six', 'Six', '2001-02-24')"); $dbh->query("INSERT INTO phptest VALUES(7, 'seven', 'Seven', '2001-02-25')"); print '6) before autocommit+rollback: '; dumptable(0); $dbh->rollback(); print '7) after autocommit+rollback: '; dumptable(0); print '8) testing that select doesn\'t disturbe opcount: '; $dbh->autoCommit(false); $dbh->simpleQuery("SELECT * FROM phptest"); $dbh->simpleQuery("SELECT a,cc FROM phptest"); $dbh->simpleQuery("SELECT b,d FROM phptest"); if (empty($dbh->transaction_opcount)) { print "ok\n"; } else { print "failed (count=$dbh->transaction_opcount)\n"; } DB-1.7.14/DB.php0000660000175100017510000012347511626162707012673 0ustar danielcdanielc * @author Tomas V.V.Cox * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version CVS: $Id: DB.php 315557 2011-08-26 14:32:35Z danielc $ * @link http://pear.php.net/package/DB */ /** * Obtain the PEAR class so it can be extended from */ require_once 'PEAR.php'; // {{{ constants // {{{ error codes /**#@+ * One of PEAR DB's portable error codes. * @see DB_common::errorCode(), DB::errorMessage() * * {@internal If you add an error code here, make sure you also add a textual * version of it in DB::errorMessage().}} */ /** * The code returned by many methods upon success */ define('DB_OK', 1); /** * Unkown error */ define('DB_ERROR', -1); /** * Syntax error */ define('DB_ERROR_SYNTAX', -2); /** * Tried to insert a duplicate value into a primary or unique index */ define('DB_ERROR_CONSTRAINT', -3); /** * An identifier in the query refers to a non-existant object */ define('DB_ERROR_NOT_FOUND', -4); /** * Tried to create a duplicate object */ define('DB_ERROR_ALREADY_EXISTS', -5); /** * The current driver does not support the action you attempted */ define('DB_ERROR_UNSUPPORTED', -6); /** * The number of parameters does not match the number of placeholders */ define('DB_ERROR_MISMATCH', -7); /** * A literal submitted did not match the data type expected */ define('DB_ERROR_INVALID', -8); /** * The current DBMS does not support the action you attempted */ define('DB_ERROR_NOT_CAPABLE', -9); /** * A literal submitted was too long so the end of it was removed */ define('DB_ERROR_TRUNCATED', -10); /** * A literal number submitted did not match the data type expected */ define('DB_ERROR_INVALID_NUMBER', -11); /** * A literal date submitted did not match the data type expected */ define('DB_ERROR_INVALID_DATE', -12); /** * Attempt to divide something by zero */ define('DB_ERROR_DIVZERO', -13); /** * A database needs to be selected */ define('DB_ERROR_NODBSELECTED', -14); /** * Could not create the object requested */ define('DB_ERROR_CANNOT_CREATE', -15); /** * Could not drop the database requested because it does not exist */ define('DB_ERROR_CANNOT_DROP', -17); /** * An identifier in the query refers to a non-existant table */ define('DB_ERROR_NOSUCHTABLE', -18); /** * An identifier in the query refers to a non-existant column */ define('DB_ERROR_NOSUCHFIELD', -19); /** * The data submitted to the method was inappropriate */ define('DB_ERROR_NEED_MORE_DATA', -20); /** * The attempt to lock the table failed */ define('DB_ERROR_NOT_LOCKED', -21); /** * The number of columns doesn't match the number of values */ define('DB_ERROR_VALUE_COUNT_ON_ROW', -22); /** * The DSN submitted has problems */ define('DB_ERROR_INVALID_DSN', -23); /** * Could not connect to the database */ define('DB_ERROR_CONNECT_FAILED', -24); /** * The PHP extension needed for this DBMS could not be found */ define('DB_ERROR_EXTENSION_NOT_FOUND',-25); /** * The present user has inadequate permissions to perform the task requestd */ define('DB_ERROR_ACCESS_VIOLATION', -26); /** * The database requested does not exist */ define('DB_ERROR_NOSUCHDB', -27); /** * Tried to insert a null value into a column that doesn't allow nulls */ define('DB_ERROR_CONSTRAINT_NOT_NULL',-29); /**#@-*/ // }}} // {{{ prepared statement-related /**#@+ * Identifiers for the placeholders used in prepared statements. * @see DB_common::prepare() */ /** * Indicates a scalar (?) placeholder was used * * Quote and escape the value as necessary. */ define('DB_PARAM_SCALAR', 1); /** * Indicates an opaque (&) placeholder was used * * The value presented is a file name. Extract the contents of that file * and place them in this column. */ define('DB_PARAM_OPAQUE', 2); /** * Indicates a misc (!) placeholder was used * * The value should not be quoted or escaped. */ define('DB_PARAM_MISC', 3); /**#@-*/ // }}} // {{{ binary data-related /**#@+ * The different ways of returning binary data from queries. */ /** * Sends the fetched data straight through to output */ define('DB_BINMODE_PASSTHRU', 1); /** * Lets you return data as usual */ define('DB_BINMODE_RETURN', 2); /** * Converts the data to hex format before returning it * * For example the string "123" would become "313233". */ define('DB_BINMODE_CONVERT', 3); /**#@-*/ // }}} // {{{ fetch modes /**#@+ * Fetch Modes. * @see DB_common::setFetchMode() */ /** * Indicates the current default fetch mode should be used * @see DB_common::$fetchmode */ 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); /** * Column data as object properties */ define('DB_FETCHMODE_OBJECT', 3); /** * For multi-dimensional results, make the column name the first level * of the array and put the row number in the second level of the array * * This is flipped from the normal behavior, which puts the row numbers * in the first level of the array and the column names in the second level. */ define('DB_FETCHMODE_FLIPPED', 4); /**#@-*/ /**#@+ * Old fetch modes. Left here for compatibility. */ define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED); define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC); define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED); /**#@-*/ // }}} // {{{ tableInfo() && autoPrepare()-related /**#@+ * The type of information to return from the tableInfo() method. * * Bitwised constants, so they can be combined using | * and removed using ^. * * @see DB_common::tableInfo() * * {@internal Since the TABLEINFO constants are bitwised, if more of them are * added in the future, make sure to adjust DB_TABLEINFO_FULL accordingly.}} */ define('DB_TABLEINFO_ORDER', 1); define('DB_TABLEINFO_ORDERTABLE', 2); define('DB_TABLEINFO_FULL', 3); /**#@-*/ /**#@+ * The type of query to create with the automatic query building methods. * @see DB_common::autoPrepare(), DB_common::autoExecute() */ define('DB_AUTOQUERY_INSERT', 1); define('DB_AUTOQUERY_UPDATE', 2); /**#@-*/ // }}} // {{{ portability modes /**#@+ * Portability Modes. * * Bitwised constants, so they can be combined using | * and removed using ^. * * @see DB_common::setOption() * * {@internal Since the PORTABILITY constants are bitwised, if more of them are * added in the future, make sure to adjust DB_PORTABILITY_ALL accordingly.}} */ /** * Turn off all portability features */ define('DB_PORTABILITY_NONE', 0); /** * Convert names of tables and fields to lower case * when using the get*(), fetch*() and tableInfo() methods */ define('DB_PORTABILITY_LOWERCASE', 1); /** * Right trim the data output by get*() and fetch*() */ define('DB_PORTABILITY_RTRIM', 2); /** * Force reporting the number of rows deleted */ define('DB_PORTABILITY_DELETE_COUNT', 4); /** * Enable hack that makes numRows() work in Oracle */ define('DB_PORTABILITY_NUMROWS', 8); /** * Makes certain error messages in certain drivers compatible * with those from other DBMS's * * + mysql, mysqli: change unique/primary key constraints * DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT * * + odbc(access): MS's ODBC driver reports 'no such field' as code * 07001, which means 'too few parameters.' When this option is on * that code gets mapped to DB_ERROR_NOSUCHFIELD. */ define('DB_PORTABILITY_ERRORS', 16); /** * Convert null values to empty strings in data output by * get*() and fetch*() */ define('DB_PORTABILITY_NULL_TO_EMPTY', 32); /** * Turn on all portability features */ define('DB_PORTABILITY_ALL', 63); /**#@-*/ // }}} // }}} // {{{ class DB /** * Database independent query interface * * 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.
 * 
* * @category Database * @package DB * @author Stig Bakken * @author Tomas V.V.Cox * @author Daniel Convissor * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB { // {{{ &factory() /** * Create a new DB object for the specified database type but don't * connect to the database * * @param string $type the database type (eg "mysql") * @param array $options an associative array of option names and values * * @return object a new DB object. A DB_Error object on failure. * * @see DB_common::setOption() */ function &factory($type, $options = false) { if (!is_array($options)) { $options = array('persistent' => $options); } if (isset($options['debug']) && $options['debug'] >= 2) { // expose php errors with sufficient debug level include_once "DB/{$type}.php"; } else { @include_once "DB/{$type}.php"; } $classname = "DB_${type}"; if (!class_exists($classname)) { $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, "Unable to include the DB/{$type}.php" . " file for '$dsn'", 'DB_Error', true); return $tmp; } @$obj = new $classname; foreach ($options as $option => $value) { $test = $obj->setOption($option, $value); if (DB::isError($test)) { return $test; } } return $obj; } // }}} // {{{ &connect() /** * Create a new DB object including a connection to the specified database * * Example 1. * * require_once 'DB.php'; * * $dsn = 'pgsql://user:password@host/database'; * $options = array( * 'debug' => 2, * 'portability' => DB_PORTABILITY_ALL, * ); * * $db =& DB::connect($dsn, $options); * if (PEAR::isError($db)) { * die($db->getMessage()); * } * * * @param mixed $dsn the string "data source name" or array in the * format returned by DB::parseDSN() * @param array $options an associative array of option names and values * * @return object a new DB object. A DB_Error object on failure. * * @uses DB_dbase::connect(), DB_fbsql::connect(), DB_ibase::connect(), * DB_ifx::connect(), DB_msql::connect(), DB_mssql::connect(), * DB_mysql::connect(), DB_mysqli::connect(), DB_oci8::connect(), * DB_odbc::connect(), DB_pgsql::connect(), DB_sqlite::connect(), * DB_sybase::connect() * * @uses DB::parseDSN(), DB_common::setOption(), PEAR::isError() */ function &connect($dsn, $options = array()) { $dsninfo = DB::parseDSN($dsn); $type = $dsninfo['phptype']; if (!is_array($options)) { /* * For backwards compatibility. $options used to be boolean, * indicating whether the connection should be persistent. */ $options = array('persistent' => $options); } if (isset($options['debug']) && $options['debug'] >= 2) { // expose php errors with sufficient debug level include_once "DB/${type}.php"; } else { @include_once "DB/${type}.php"; } $classname = "DB_${type}"; if (!class_exists($classname)) { $tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null, "Unable to include the DB/{$type}.php" . " file for '$dsn'", 'DB_Error', true); return $tmp; } @$obj = new $classname; foreach ($options as $option => $value) { $test = $obj->setOption($option, $value); if (DB::isError($test)) { return $test; } } $err = $obj->connect($dsninfo, $obj->getOption('persistent')); if (DB::isError($err)) { if (is_array($dsn)) { $err->addUserInfo(DB::getDSNString($dsn, true)); } else { $err->addUserInfo($dsn); } return $err; } return $obj; } // }}} // {{{ apiVersion() /** * Return the DB API version * * @return string the DB API version number */ function apiVersion() { return '1.7.14'; } // }}} // {{{ isError() /** * Determines if a variable is a DB_Error object * * @param mixed $value the variable to check * * @return bool whether $value is DB_Error object */ function isError($value) { return is_object($value) && is_a($value, 'DB_Error'); } // }}} // {{{ isConnection() /** * Determines if a value is a DB_ object * * @param mixed $value the value to test * * @return bool whether $value is a DB_ object */ function isConnection($value) { return (is_object($value) && is_subclass_of($value, 'db_common') && method_exists($value, 'simpleQuery')); } // }}} // {{{ isManip() /** * Tell whether a query is a data manipulation or data definition query * * Examples of data manipulation queries are INSERT, UPDATE and DELETE. * Examples of data definition queries are CREATE, DROP, ALTER, GRANT, * REVOKE. * * @param string $query the query * * @return boolean whether $query is a data manipulation query */ function isManip($query) { $manips = 'INSERT|UPDATE|DELETE|REPLACE|' . 'CREATE|DROP|' . 'LOAD DATA|SELECT .* INTO .* FROM|COPY|' . 'ALTER|GRANT|REVOKE|' . 'LOCK|UNLOCK'; if (preg_match('/^\s*"?(' . $manips . ')\s+/i', $query)) { return true; } return false; } // }}} // {{{ errorMessage() /** * Return a textual error message for a DB error code * * @param integer $value the DB error code * * @return string the error message or false if the error code was * not recognized */ function errorMessage($value) { static $errorMessages; if (!isset($errorMessages)) { $errorMessages = array( DB_ERROR => 'unknown error', DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions', DB_ERROR_ALREADY_EXISTS => 'already exists', DB_ERROR_CANNOT_CREATE => 'can not create', DB_ERROR_CANNOT_DROP => 'can not drop', DB_ERROR_CONNECT_FAILED => 'connect failed', DB_ERROR_CONSTRAINT => 'constraint violation', DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', DB_ERROR_DIVZERO => 'division by zero', DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', DB_ERROR_INVALID => 'invalid', DB_ERROR_INVALID_DATE => 'invalid date or time', DB_ERROR_INVALID_DSN => 'invalid DSN', DB_ERROR_INVALID_NUMBER => 'invalid number', DB_ERROR_MISMATCH => 'mismatch', DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied', DB_ERROR_NODBSELECTED => 'no database selected', DB_ERROR_NOSUCHDB => 'no such database', 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_TRUNCATED => 'truncated', DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', DB_OK => 'no error', ); } if (DB::isError($value)) { $value = $value->getCode(); } return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[DB_ERROR]; } // }}} // {{{ parseDSN() /** * Parse a data source name * * Additional keys can be added by appending a URI query string to the * end of the DSN. * * The format of the supplied DSN is in its fullest form: * * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true * * * Most variations are allowed: * * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644 * phptype://username:password@hostspec/database_name * phptype://username:password@hostspec * phptype://username@hostspec * phptype://hostspec/database * phptype://hostspec * phptype(dbsyntax) * phptype * * * @param string $dsn Data Source Name to be parsed * * @return array an associative array with the following keys: * + 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 */ function parseDSN($dsn) { $parsed = array( 'phptype' => false, 'dbsyntax' => false, 'username' => false, 'password' => false, 'protocol' => false, 'hostspec' => false, 'port' => false, 'socket' => false, 'database' => false, ); if (is_array($dsn)) { $dsn = array_merge($parsed, $dsn); if (!$dsn['dbsyntax']) { $dsn['dbsyntax'] = $dsn['phptype']; } return $dsn; } // Find phptype and dbsyntax if (($pos = strpos($dsn, '://')) !== false) { $str = substr($dsn, 0, $pos); $dsn = substr($dsn, $pos + 3); } else { $str = $dsn; $dsn = null; } // Get phptype and dbsyntax // $str => phptype(dbsyntax) if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { $parsed['phptype'] = $arr[1]; $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2]; } else { $parsed['phptype'] = $str; $parsed['dbsyntax'] = $str; } if (!count($dsn)) { return $parsed; } // Get (if found): username and password // $dsn => username:password@protocol+hostspec/database if (($at = strrpos($dsn,'@')) !== false) { $str = substr($dsn, 0, $at); $dsn = substr($dsn, $at + 1); if (($pos = strpos($str, ':')) !== false) { $parsed['username'] = rawurldecode(substr($str, 0, $pos)); $parsed['password'] = rawurldecode(substr($str, $pos + 1)); } else { $parsed['username'] = rawurldecode($str); } } // Find protocol and hostspec if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { // $dsn => proto(proto_opts)/database $proto = $match[1]; $proto_opts = $match[2] ? $match[2] : false; $dsn = $match[3]; } else { // $dsn => protocol+hostspec/database (old format) if (strpos($dsn, '+') !== false) { list($proto, $dsn) = explode('+', $dsn, 2); } if (strpos($dsn, '/') !== false) { list($proto_opts, $dsn) = explode('/', $dsn, 2); } else { $proto_opts = $dsn; $dsn = null; } } // process the different protocol options $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; $proto_opts = rawurldecode($proto_opts); if (strpos($proto_opts, ':') !== false) { list($proto_opts, $parsed['port']) = explode(':', $proto_opts); } if ($parsed['protocol'] == 'tcp') { $parsed['hostspec'] = $proto_opts; } elseif ($parsed['protocol'] == 'unix') { $parsed['socket'] = $proto_opts; } // Get dabase if any // $dsn => database if ($dsn) { if (($pos = strpos($dsn, '?')) === false) { // /database $parsed['database'] = rawurldecode($dsn); } else { // /database?param1=value1¶m2=value2 $parsed['database'] = rawurldecode(substr($dsn, 0, $pos)); $dsn = substr($dsn, $pos + 1); if (strpos($dsn, '&') !== false) { $opts = explode('&', $dsn); } else { // database?param1=value1 $opts = array($dsn); } foreach ($opts as $opt) { list($key, $value) = explode('=', $opt); if (!isset($parsed[$key])) { // don't allow params overwrite $parsed[$key] = rawurldecode($value); } } } } return $parsed; } // }}} // {{{ getDSNString() /** * Returns the given DSN in a string format suitable for output. * * @param array|string the DSN to parse and format * @param boolean true to hide the password, false to include it * @return string */ function getDSNString($dsn, $hidePassword) { /* Calling parseDSN will ensure that we have all the array elements * defined, and means that we deal with strings and array in the same * manner. */ $dsnArray = DB::parseDSN($dsn); if ($hidePassword) { $dsnArray['password'] = 'PASSWORD'; } /* Protocol is special-cased, as using the default "tcp" along with an * Oracle TNS connection string fails. */ if (is_string($dsn) && strpos($dsn, 'tcp') === false && $dsnArray['protocol'] == 'tcp') { $dsnArray['protocol'] = false; } // Now we just have to construct the actual string. This is ugly. $dsnString = $dsnArray['phptype']; if ($dsnArray['dbsyntax']) { $dsnString .= '('.$dsnArray['dbsyntax'].')'; } $dsnString .= '://' .$dsnArray['username'] .':' .$dsnArray['password'] .'@' .$dsnArray['protocol']; if ($dsnArray['socket']) { $dsnString .= '('.$dsnArray['socket'].')'; } if ($dsnArray['protocol'] && $dsnArray['hostspec']) { $dsnString .= '+'; } $dsnString .= $dsnArray['hostspec']; if ($dsnArray['port']) { $dsnString .= ':'.$dsnArray['port']; } $dsnString .= '/'.$dsnArray['database']; /* Option handling. Unfortunately, parseDSN simply places options into * the top-level array, so we'll first get rid of the fields defined by * DB and see what's left. */ unset($dsnArray['phptype'], $dsnArray['dbsyntax'], $dsnArray['username'], $dsnArray['password'], $dsnArray['protocol'], $dsnArray['socket'], $dsnArray['hostspec'], $dsnArray['port'], $dsnArray['database'] ); if (count($dsnArray) > 0) { $dsnString .= '?'; $i = 0; foreach ($dsnArray as $key => $value) { if (++$i > 1) { $dsnString .= '&'; } $dsnString .= $key.'='.$value; } } return $dsnString; } // }}} } // }}} // {{{ class DB_Error /** * DB_Error implements a class for reporting portable database error * messages * * @category Database * @package DB * @author Stig Bakken * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_Error extends PEAR_Error { // {{{ constructor /** * DB_Error constructor * * @param mixed $code DB error code, or string with error message * @param int $mode what "error mode" to operate in * @param int $level what error level to use for $mode & * PEAR_ERROR_TRIGGER * @param mixed $debuginfo additional debug info, such as the last query * * @see 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); } } // }}} } // }}} // {{{ class DB_result /** * 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. * * @category Database * @package DB * @author Stig Bakken * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB */ class DB_result { // {{{ properties /** * Should results be freed automatically when there are no more rows? * @var boolean * @see DB_common::$options */ var $autofree; /** * A reference to the DB_ object * @var object */ var $dbh; /** * The current default fetch mode * @var integer * @see DB_common::$fetchmode */ var $fetchmode; /** * The name of the class into which results should be fetched when * DB_FETCHMODE_OBJECT is in effect * * @var string * @see DB_common::$fetchmode_object_class */ var $fetchmode_object_class; /** * The number of rows to fetch from a limit query * @var integer */ var $limit_count = null; /** * The row to start fetching from in limit queries * @var integer */ var $limit_from = null; /** * The execute parameters that created this result * @var array * @since Property available since Release 1.7.0 */ var $parameters; /** * The query string that created this result * * Copied here incase it changes in $dbh, which is referenced * * @var string * @since Property available since Release 1.7.0 */ var $query; /** * The query result resource id created by PHP * @var resource */ var $result; /** * The present row being dealt with * @var integer */ var $row_counter = null; /** * The prepared statement resource id created by PHP in $dbh * * This resource is only available when the result set was created using * a driver's native execute() method, not PEAR DB's emulated one. * * Copied here incase it changes in $dbh, which is referenced * * {@internal Mainly here because the InterBase/Firebird API is only * able to retrieve data from result sets if the statemnt handle is * still in scope.}} * * @var resource * @since Property available since Release 1.7.0 */ var $statement; // }}} // {{{ constructor /** * This constructor sets the object's properties * * @param object &$dbh the DB object reference * @param resource $result the result resource id * @param array $options an associative array with result options * * @return void */ function DB_result(&$dbh, $result, $options = array()) { $this->autofree = $dbh->options['autofree']; $this->dbh = &$dbh; $this->fetchmode = $dbh->fetchmode; $this->fetchmode_object_class = $dbh->fetchmode_object_class; $this->parameters = $dbh->last_parameters; $this->query = $dbh->last_query; $this->result = $result; $this->statement = empty($dbh->last_stmt) ? null : $dbh->last_stmt; foreach ($options as $key => $value) { $this->setOption($key, $value); } } /** * Set options for the DB_result object * * @param string $key the option to set * @param mixed $value the value to set the option to * * @return void */ function setOption($key, $value = null) { switch ($key) { case 'limit_from': $this->limit_from = $value; break; case 'limit_count': $this->limit_count = $value; } } // }}} // {{{ fetchRow() /** * Fetch a row of data and return it by reference into an array * * The type of array returned can be controlled either by setting this * method's $fetchmode parameter or by changing the default * fetch mode setFetchMode() before calling this method. * * There are two options for standardizing the information returned * from databases, ensuring their values are consistent when changing * DBMS's. These portability options can be turned on when creating a * new DB object or by using setOption(). * * + DB_PORTABILITY_LOWERCASE * convert names of fields to lower case * * + DB_PORTABILITY_RTRIM * right trim the data * * @param int $fetchmode the constant indicating how to format the data * @param int $rownum the row number to fetch (index starts at 0) * * @return mixed an array or object containing the row's data, * NULL when the end of the result set is reached * or a DB_Error object on failure. * * @see DB_common::setOption(), DB_common::setFetchMode() */ function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) { if ($fetchmode === DB_FETCHMODE_DEFAULT) { $fetchmode = $this->fetchmode; } if ($fetchmode === DB_FETCHMODE_OBJECT) { $fetchmode = DB_FETCHMODE_ASSOC; $object_class = $this->fetchmode_object_class; } if (is_null($rownum) && $this->limit_from !== null) { if ($this->row_counter === null) { $this->row_counter = $this->limit_from; // Skip rows if ($this->dbh->features['limit'] === false) { $i = 0; while ($i++ < $this->limit_from) { $this->dbh->fetchInto($this->result, $arr, $fetchmode); } } } if ($this->row_counter >= ($this->limit_from + $this->limit_count)) { if ($this->autofree) { $this->free(); } $tmp = null; return $tmp; } if ($this->dbh->features['limit'] === 'emulate') { $rownum = $this->row_counter; } $this->row_counter++; } $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); if ($res === DB_OK) { if (isset($object_class)) { // The default mode is specified in the // DB_common::fetchmode_object_class property if ($object_class == 'stdClass') { $arr = (object) $arr; } else { $arr = new $object_class($arr); } } return $arr; } if ($res == null && $this->autofree) { $this->free(); } return $res; } // }}} // {{{ fetchInto() /** * Fetch a row of data into an array which is passed by reference * * The type of array returned can be controlled either by setting this * method's $fetchmode parameter or by changing the default * fetch mode setFetchMode() before calling this method. * * There are two options for standardizing the information returned * from databases, ensuring their values are consistent when changing * DBMS's. These portability options can be turned on when creating a * new DB object or by using setOption(). * * + DB_PORTABILITY_LOWERCASE * convert names of fields to lower case * * + DB_PORTABILITY_RTRIM * right trim the data * * @param array &$arr the variable where the data should be placed * @param int $fetchmode the constant indicating how to format the data * @param int $rownum the row number to fetch (index starts at 0) * * @return mixed DB_OK if a row is processed, NULL when the end of the * result set is reached or a DB_Error object on failure * * @see DB_common::setOption(), DB_common::setFetchMode() */ function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum = null) { if ($fetchmode === DB_FETCHMODE_DEFAULT) { $fetchmode = $this->fetchmode; } if ($fetchmode === DB_FETCHMODE_OBJECT) { $fetchmode = DB_FETCHMODE_ASSOC; $object_class = $this->fetchmode_object_class; } if (is_null($rownum) && $this->limit_from !== null) { if ($this->row_counter === null) { $this->row_counter = $this->limit_from; // Skip rows if ($this->dbh->features['limit'] === false) { $i = 0; while ($i++ < $this->limit_from) { $this->dbh->fetchInto($this->result, $arr, $fetchmode); } } } if ($this->row_counter >= ( $this->limit_from + $this->limit_count)) { if ($this->autofree) { $this->free(); } return null; } if ($this->dbh->features['limit'] === 'emulate') { $rownum = $this->row_counter; } $this->row_counter++; } $res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum); if ($res === DB_OK) { if (isset($object_class)) { // default mode specified in the // DB_common::fetchmode_object_class property if ($object_class == 'stdClass') { $arr = (object) $arr; } else { $arr = new $object_class($arr); } } return DB_OK; } if ($res == null && $this->autofree) { $this->free(); } return $res; } // }}} // {{{ numCols() /** * Get the the number of columns in a result set * * @return int the number of columns. A DB_Error object on failure. */ function numCols() { return $this->dbh->numCols($this->result); } // }}} // {{{ numRows() /** * Get the number of rows in a result set * * @return int the number of rows. A DB_Error object on failure. */ function numRows() { if ($this->dbh->features['numrows'] === 'emulate' && $this->dbh->options['portability'] & DB_PORTABILITY_NUMROWS) { if ($this->dbh->features['prepare']) { $res = $this->dbh->query($this->query, $this->parameters); } else { $res = $this->dbh->query($this->query); } if (DB::isError($res)) { return $res; } $i = 0; while ($res->fetchInto($tmp, DB_FETCHMODE_ORDERED)) { $i++; } $count = $i; } else { $count = $this->dbh->numRows($this->result); } /* fbsql is checked for here because limit queries are implemented * using a TOP() function, which results in fbsql_num_rows still * returning the total number of rows that would have been returned, * rather than the real number. As a result, we'll just do the limit * calculations for fbsql in the same way as a database with emulated * limits. Unfortunately, we can't just do this in DB_fbsql::numRows() * because that only gets the result resource, rather than the full * DB_Result object. */ if (($this->dbh->features['limit'] === 'emulate' && $this->limit_from !== null) || $this->dbh->phptype == 'fbsql') { $limit_count = is_null($this->limit_count) ? $count : $this->limit_count; if ($count < $this->limit_from) { $count = 0; } elseif ($count < ($this->limit_from + $limit_count)) { $count -= $this->limit_from; } else { $count = $limit_count; } } return $count; } // }}} // {{{ nextResult() /** * Get the next result if a batch of queries was executed * * @return bool true if a new result is available or false if not */ function nextResult() { return $this->dbh->nextResult($this->result); } // }}} // {{{ free() /** * Frees the resources allocated for this result set * * @return bool true on success. A DB_Error object on failure. */ function free() { $err = $this->dbh->freeResult($this->result); if (DB::isError($err)) { return $err; } $this->result = false; $this->statement = false; return true; } // }}} // {{{ tableInfo() /** * @see DB_common::tableInfo() * @deprecated Method deprecated some time before Release 1.2 */ function tableInfo($mode = null) { if (is_string($mode)) { return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA); } return $this->dbh->tableInfo($this, $mode); } // }}} // {{{ getQuery() /** * Determine the query string that created this result * * @return string the query string * * @since Method available since Release 1.7.0 */ function getQuery() { return $this->query; } // }}} // {{{ getRowCounter() /** * Tells which row number is currently being processed * * @return integer the current row being looked at. Starts at 1. */ function getRowCounter() { return $this->row_counter; } // }}} } // }}} // {{{ class DB_row /** * PEAR DB Row Object * * The object contains a row of data from a result set. Each column's data * is placed in a property named for the column. * * @category Database * @package DB * @author Stig Bakken * @copyright 1997-2007 The PHP Group * @license http://www.php.net/license/3_0.txt PHP License 3.0 * @version Release: 1.7.14 * @link http://pear.php.net/package/DB * @see DB_common::setFetchMode() */ class DB_row { // {{{ constructor /** * The constructor places a row's data into properties of this object * * @param array the array containing the row's data * * @return void */ function DB_row(&$arr) { foreach ($arr as $key => $value) { $this->$key = &$arr[$key]; } } // }}} } // }}} /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ ?>