pax_global_header00006660000000000000000000000064121366602110014510gustar00rootroot0000000000000052 comment=ff49fcb3b398b11a8e9498fee267dec7754d9ed3 php-mdb2-2.5.0b5/000077500000000000000000000000001213666021100133545ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/000077500000000000000000000000001213666021100147275ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/LICENSE000066400000000000000000000055071213666021100157430ustar00rootroot00000000000000// +----------------------------------------------------------------------+ // | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox, | // | Stig. S. Bakken, Lukas Smith | // | All rights reserved. | // +----------------------------------------------------------------------+ // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB | // | API as well as database abstraction for PHP applications. | // | This LICENSE is in the BSD license style. | // | | // | Redistribution and use in source and binary forms, with or without | // | modification, are permitted provided that the following conditions | // | are met: | // | | // | Redistributions of source code must retain the above copyright | // | notice, this list of conditions and the following disclaimer. | // | | // | Redistributions in binary form must reproduce the above copyright | // | notice, this list of conditions and the following disclaimer in the | // | documentation and/or other materials provided with the distribution. | // | | // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, | // | Lukas Smith nor the names of his contributors may be used to endorse | // | or promote products derived from this software without specific prior| // | written permission. | // | | // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS| // | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY| // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | // | POSSIBILITY OF SUCH DAMAGE. | // +----------------------------------------------------------------------+php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2.php000066400000000000000000004522221213666021100161330ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: MDB2.php 328183 2012-10-29 15:10:42Z danielc $ // /** * @package MDB2 * @category Database * @author Lukas Smith */ require_once 'PEAR.php'; // {{{ Error constants /** * The method mapErrorCode in each MDB2_dbtype implementation maps * native error codes to one of these. * * If you add an error code here, make sure you also add a textual * version of it in MDB2::errorMessage(). */ define('MDB2_OK', true); define('MDB2_ERROR', -1); define('MDB2_ERROR_SYNTAX', -2); define('MDB2_ERROR_CONSTRAINT', -3); define('MDB2_ERROR_NOT_FOUND', -4); define('MDB2_ERROR_ALREADY_EXISTS', -5); define('MDB2_ERROR_UNSUPPORTED', -6); define('MDB2_ERROR_MISMATCH', -7); define('MDB2_ERROR_INVALID', -8); define('MDB2_ERROR_NOT_CAPABLE', -9); define('MDB2_ERROR_TRUNCATED', -10); define('MDB2_ERROR_INVALID_NUMBER', -11); define('MDB2_ERROR_INVALID_DATE', -12); define('MDB2_ERROR_DIVZERO', -13); define('MDB2_ERROR_NODBSELECTED', -14); define('MDB2_ERROR_CANNOT_CREATE', -15); define('MDB2_ERROR_CANNOT_DELETE', -16); define('MDB2_ERROR_CANNOT_DROP', -17); define('MDB2_ERROR_NOSUCHTABLE', -18); define('MDB2_ERROR_NOSUCHFIELD', -19); define('MDB2_ERROR_NEED_MORE_DATA', -20); define('MDB2_ERROR_NOT_LOCKED', -21); define('MDB2_ERROR_VALUE_COUNT_ON_ROW', -22); define('MDB2_ERROR_INVALID_DSN', -23); define('MDB2_ERROR_CONNECT_FAILED', -24); define('MDB2_ERROR_EXTENSION_NOT_FOUND',-25); define('MDB2_ERROR_NOSUCHDB', -26); define('MDB2_ERROR_ACCESS_VIOLATION', -27); define('MDB2_ERROR_CANNOT_REPLACE', -28); define('MDB2_ERROR_CONSTRAINT_NOT_NULL',-29); define('MDB2_ERROR_DEADLOCK', -30); define('MDB2_ERROR_CANNOT_ALTER', -31); define('MDB2_ERROR_MANAGER', -32); define('MDB2_ERROR_MANAGER_PARSE', -33); define('MDB2_ERROR_LOADMODULE', -34); define('MDB2_ERROR_INSUFFICIENT_DATA', -35); define('MDB2_ERROR_NO_PERMISSION', -36); define('MDB2_ERROR_DISCONNECT_FAILED', -37); // }}} // {{{ Verbose constants /** * These are just helper constants to more verbosely express parameters to prepare() */ define('MDB2_PREPARE_MANIP', false); define('MDB2_PREPARE_RESULT', null); // }}} // {{{ Fetchmode constants /** * This is a special constant that tells MDB2 the user hasn't specified * any particular get mode, so the default should be used. */ define('MDB2_FETCHMODE_DEFAULT', 0); /** * Column data indexed by numbers, ordered from 0 and up */ define('MDB2_FETCHMODE_ORDERED', 1); /** * Column data indexed by column names */ define('MDB2_FETCHMODE_ASSOC', 2); /** * Column data as object properties */ define('MDB2_FETCHMODE_OBJECT', 3); /** * For multi-dimensional results: normally the first level of arrays * is the row number, and the second level indexed by column number or name. * MDB2_FETCHMODE_FLIPPED switches this order, so the first level of arrays * is the column name, and the second level the row number. */ define('MDB2_FETCHMODE_FLIPPED', 4); // }}} // {{{ Portability mode constants /** * Portability: turn off all portability features. * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_NONE', 0); /** * Portability: convert names of tables and fields to case defined in the * "field_case" option when using the query*(), fetch*() and tableInfo() methods. * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_FIX_CASE', 1); /** * Portability: right trim the data output by query*() and fetch*(). * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_RTRIM', 2); /** * Portability: force reporting the number of rows deleted. * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_DELETE_COUNT', 4); /** * Portability: not needed in MDB2 (just left here for compatibility to DB) * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_NUMROWS', 8); /** * Portability: makes certain error messages in certain drivers compatible * with those from other DBMS's. * * + mysql, mysqli: change unique/primary key constraints * MDB2_ERROR_ALREADY_EXISTS -> MDB2_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 MDB2_ERROR_NOSUCHFIELD. * * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_ERRORS', 16); /** * Portability: convert empty values to null strings in data output by * query*() and fetch*(). * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_EMPTY_TO_NULL', 32); /** * Portability: removes database/table qualifiers from associative indexes * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES', 64); /** * Portability: turn on all portability features. * @see MDB2_Driver_Common::setOption() */ define('MDB2_PORTABILITY_ALL', 127); // }}} // {{{ Globals for class instance tracking /** * These are global variables that are used to track the various class instances */ $GLOBALS['_MDB2_databases'] = array(); $GLOBALS['_MDB2_dsninfo_default'] = array( 'phptype' => false, 'dbsyntax' => false, 'username' => false, 'password' => false, 'protocol' => false, 'hostspec' => false, 'port' => false, 'socket' => false, 'database' => false, 'mode' => false, ); // }}} // {{{ class MDB2 /** * The main 'MDB2' 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 MDB2 is as follows (indentation means inheritance): * * MDB2 The main MDB2 class. This is simply a utility class * with some 'static' methods for creating MDB2 objects as * well as common utility functions for other MDB2 classes. * * MDB2_Driver_Common The base for each MDB2 implementation. Provides default * | implementations (in OO lingo virtual methods) for * | the actual DB implementations as well as a bunch of * | query utility functions. * | * +-MDB2_Driver_mysql The MDB2 implementation for MySQL. Inherits MDB2_Driver_Common. * When calling MDB2::factory or MDB2::connect for MySQL * connections, the object returned is an instance of this * class. * +-MDB2_Driver_pgsql The MDB2 implementation for PostGreSQL. Inherits MDB2_Driver_Common. * When calling MDB2::factory or MDB2::connect for PostGreSQL * connections, the object returned is an instance of this * class. * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2 { // {{{ function setOptions($db, $options) /** * set option array in an exiting database object * * @param MDB2_Driver_Common MDB2 object * @param array An associative array of option names and their values. * * @return mixed MDB2_OK or a PEAR Error object * * @access public */ static function setOptions($db, $options) { if (is_array($options)) { foreach ($options as $option => $value) { $test = $db->setOption($option, $value); if (MDB2::isError($test)) { return $test; } } } return MDB2_OK; } // }}} // {{{ function classExists($classname) /** * Checks if a class exists without triggering __autoload * * @param string classname * * @return bool true success and false on error * @static * @access public */ static function classExists($classname) { return class_exists($classname, false); } // }}} // {{{ function loadClass($class_name, $debug) /** * Loads a PEAR class. * * @param string classname to load * @param bool if errors should be suppressed * * @return mixed true success or PEAR_Error on failure * * @access public */ static function loadClass($class_name, $debug) { if (!MDB2::classExists($class_name)) { $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php'; if ($debug) { $include = include_once($file_name); } else { $include = @include_once($file_name); } if (!$include) { if (!MDB2::fileExists($file_name)) { $msg = "unable to find package '$class_name' file '$file_name'"; } else { $msg = "unable to load class '$class_name' from file '$file_name'"; } $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg); return $err; } if (!MDB2::classExists($class_name)) { $msg = "unable to load class '$class_name' from file '$file_name'"; $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg); return $err; } } return MDB2_OK; } // }}} // {{{ function factory($dsn, $options = false) /** * Create a new MDB2 object for the specified database type * * @param mixed 'data source name', see the MDB2::parseDSN * method for a description of the dsn format. * Can also be specified as an array of the * format returned by MDB2::parseDSN. * @param array An associative array of option names and * their values. * * @return mixed a newly created MDB2 object, or false on error * * @access public */ static function factory($dsn, $options = false) { $dsninfo = MDB2::parseDSN($dsn); if (empty($dsninfo['phptype'])) { $err = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'no RDBMS driver specified'); return $err; } $class_name = 'MDB2_Driver_'.$dsninfo['phptype']; $debug = (!empty($options['debug'])); $err = MDB2::loadClass($class_name, $debug); if (MDB2::isError($err)) { return $err; } $db = new $class_name(); $db->setDSN($dsninfo); $err = MDB2::setOptions($db, $options); if (MDB2::isError($err)) { return $err; } return $db; } // }}} // {{{ function connect($dsn, $options = false) /** * Create a new MDB2_Driver_* connection object and connect to the specified * database * * @param mixed $dsn 'data source name', see the MDB2::parseDSN * method for a description of the dsn format. * Can also be specified as an array of the * format returned by MDB2::parseDSN. * @param array $options An associative array of option names and * their values. * * @return mixed a newly created MDB2 connection object, or a MDB2 * error object on error * * @access public * @see MDB2::parseDSN */ static function connect($dsn, $options = false) { $db = MDB2::factory($dsn, $options); if (MDB2::isError($db)) { return $db; } $err = $db->connect(); if (MDB2::isError($err)) { $dsn = $db->getDSN('string', 'xxx'); $db->disconnect(); $err->addUserInfo($dsn); return $err; } return $db; } // }}} // {{{ function singleton($dsn = null, $options = false) /** * Returns a MDB2 connection with the requested DSN. * A new MDB2 connection object is only created if no object with the * requested DSN exists yet. * * @param mixed 'data source name', see the MDB2::parseDSN * method for a description of the dsn format. * Can also be specified as an array of the * format returned by MDB2::parseDSN. * @param array An associative array of option names and * their values. * * @return mixed a newly created MDB2 connection object, or a MDB2 * error object on error * * @access public * @see MDB2::parseDSN */ static function singleton($dsn = null, $options = false) { if ($dsn) { $dsninfo = MDB2::parseDSN($dsn); $dsninfo = array_merge($GLOBALS['_MDB2_dsninfo_default'], $dsninfo); $keys = array_keys($GLOBALS['_MDB2_databases']); for ($i=0, $j=count($keys); $i<$j; ++$i) { if (isset($GLOBALS['_MDB2_databases'][$keys[$i]])) { $tmp_dsn = $GLOBALS['_MDB2_databases'][$keys[$i]]->getDSN('array'); if (count(array_diff_assoc($tmp_dsn, $dsninfo)) == 0) { MDB2::setOptions($GLOBALS['_MDB2_databases'][$keys[$i]], $options); return $GLOBALS['_MDB2_databases'][$keys[$i]]; } } } } elseif (is_array($GLOBALS['_MDB2_databases']) && reset($GLOBALS['_MDB2_databases'])) { return $GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])]; } $db = MDB2::factory($dsn, $options); return $db; } // }}} // {{{ function areEquals() /** * It looks like there's a memory leak in array_diff() in PHP 5.1.x, * so use this method instead. * @see http://pear.php.net/bugs/bug.php?id=11790 * * @param array $arr1 * @param array $arr2 * @return boolean */ static function areEquals($arr1, $arr2) { if (count($arr1) != count($arr2)) { return false; } foreach (array_keys($arr1) as $k) { if (!array_key_exists($k, $arr2) || $arr1[$k] != $arr2[$k]) { return false; } } return true; } // }}} // {{{ function loadFile($file) /** * load a file (like 'Date') * * @param string $file name of the file in the MDB2 directory (without '.php') * * @return string name of the file that was included * * @access public */ static function loadFile($file) { $file_name = 'MDB2'.DIRECTORY_SEPARATOR.$file.'.php'; if (!MDB2::fileExists($file_name)) { return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'unable to find: '.$file_name); } if (!include_once($file_name)) { return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'unable to load driver class: '.$file_name); } return $file_name; } // }}} // {{{ function apiVersion() /** * Return the MDB2 API version * * @return string the MDB2 API version number * * @access public */ static function apiVersion() { return '2.5.0b5'; } // }}} // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) /** * This method is used to communicate an error and invoke error * callbacks etc. Basically a wrapper for PEAR::raiseError * without the message string. * * @param mixed int error code * * @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. * * @return PEAR_Error instance of a PEAR Error object * * @access private * @see PEAR_Error */ public static function &raiseError($code = null, $mode = null, $options = null, $userinfo = null, $dummy1 = null, $dummy2 = null, $dummy3 = false) { $pear = new PEAR; $err =& $pear->raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true); return $err; } // }}} // {{{ function isError($data, $code = null) /** * Tell whether a value is a MDB2 error. * * @param mixed the value to test * @param int if is an error object, return true * only if $code is a string and * $db->getMessage() == $code or * $code is an integer and $db->getCode() == $code * * @return bool true if parameter is an error * * @access public */ static function isError($data, $code = null) { if ($data instanceof MDB2_Error) { if (null === $code) { return true; } if (is_string($code)) { return $data->getMessage() === $code; } return in_array($data->getCode(), (array)$code); } return false; } // }}} // {{{ function isConnection($value) /** * Tell whether a value is a MDB2 connection * * @param mixed value to test * * @return bool whether $value is a MDB2 connection * @access public */ static function isConnection($value) { return ($value instanceof MDB2_Driver_Common); } // }}} // {{{ function isResult($value) /** * Tell whether a value is a MDB2 result * * @param mixed $value value to test * * @return bool whether $value is a MDB2 result * * @access public */ static function isResult($value) { return ($value instanceof MDB2_Result); } // }}} // {{{ function isResultCommon($value) /** * Tell whether a value is a MDB2 result implementing the common interface * * @param mixed $value value to test * * @return bool whether $value is a MDB2 result implementing the common interface * * @access public */ static function isResultCommon($value) { return ($value instanceof MDB2_Result_Common); } // }}} // {{{ function isStatement($value) /** * Tell whether a value is a MDB2 statement interface * * @param mixed value to test * * @return bool whether $value is a MDB2 statement interface * * @access public */ static function isStatement($value) { return ($value instanceof MDB2_Statement_Common); } // }}} // {{{ function errorMessage($value = null) /** * Return a textual error message for a MDB2 error code * * @param int|array integer error code, null to get the current error code-message map, or an array with a new error code-message map * * @return string error message, or false if the error code was * not recognized * * @access public */ static function errorMessage($value = null) { static $errorMessages; if (is_array($value)) { $errorMessages = $value; return MDB2_OK; } if (!isset($errorMessages)) { $errorMessages = array( MDB2_OK => 'no error', MDB2_ERROR => 'unknown error', MDB2_ERROR_ALREADY_EXISTS => 'already exists', MDB2_ERROR_CANNOT_CREATE => 'can not create', MDB2_ERROR_CANNOT_ALTER => 'can not alter', MDB2_ERROR_CANNOT_REPLACE => 'can not replace', MDB2_ERROR_CANNOT_DELETE => 'can not delete', MDB2_ERROR_CANNOT_DROP => 'can not drop', MDB2_ERROR_CONSTRAINT => 'constraint violation', MDB2_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint', MDB2_ERROR_DIVZERO => 'division by zero', MDB2_ERROR_INVALID => 'invalid', MDB2_ERROR_INVALID_DATE => 'invalid date or time', MDB2_ERROR_INVALID_NUMBER => 'invalid number', MDB2_ERROR_MISMATCH => 'mismatch', MDB2_ERROR_NODBSELECTED => 'no database selected', MDB2_ERROR_NOSUCHFIELD => 'no such field', MDB2_ERROR_NOSUCHTABLE => 'no such table', MDB2_ERROR_NOT_CAPABLE => 'MDB2 backend not capable', MDB2_ERROR_NOT_FOUND => 'not found', MDB2_ERROR_NOT_LOCKED => 'not locked', MDB2_ERROR_SYNTAX => 'syntax error', MDB2_ERROR_UNSUPPORTED => 'not supported', MDB2_ERROR_VALUE_COUNT_ON_ROW => 'value count on row', MDB2_ERROR_INVALID_DSN => 'invalid DSN', MDB2_ERROR_CONNECT_FAILED => 'connect failed', MDB2_ERROR_NEED_MORE_DATA => 'insufficient data supplied', MDB2_ERROR_EXTENSION_NOT_FOUND=> 'extension not found', MDB2_ERROR_NOSUCHDB => 'no such database', MDB2_ERROR_ACCESS_VIOLATION => 'insufficient permissions', MDB2_ERROR_LOADMODULE => 'error while including on demand module', MDB2_ERROR_TRUNCATED => 'truncated', MDB2_ERROR_DEADLOCK => 'deadlock detected', MDB2_ERROR_NO_PERMISSION => 'no permission', MDB2_ERROR_DISCONNECT_FAILED => 'disconnect failed', ); } if (null === $value) { return $errorMessages; } if (MDB2::isError($value)) { $value = $value->getCode(); } return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[MDB2_ERROR]; } // }}} // {{{ function parseDSN($dsn) /** * 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 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 * * @access public * @author Tomas V.V.Cox */ static function parseDSN($dsn) { $parsed = $GLOBALS['_MDB2_dsninfo_default']; 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 // $dsn => proto(proto_opts)/database if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { $proto = $match[1]; $proto_opts = $match[2] ? $match[2] : false; $dsn = $match[3]; // $dsn => protocol+hostspec/database (old format) } else { if (strpos($dsn, '+') !== false) { list($proto, $dsn) = explode('+', $dsn, 2); } if ( strpos($dsn, '//') === 0 && strpos($dsn, '/', 2) !== false && $parsed['phptype'] == 'oci8' ) { //oracle's "Easy Connect" syntax: //"username/password@[//]host[:port][/service_name]" //e.g. "scott/tiger@//mymachine:1521/oracle" $proto_opts = $dsn; $pos = strrpos($proto_opts, '/'); $dsn = substr($proto_opts, $pos + 1); $proto_opts = substr($proto_opts, 0, $pos); } elseif (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) { // /database if (($pos = strpos($dsn, '?')) === false) { $parsed['database'] = rawurldecode($dsn); // /database?param1=value1¶m2=value2 } else { $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 (!array_key_exists($key, $parsed) || false === $parsed[$key]) { // don't allow params overwrite $parsed[$key] = rawurldecode($value); } } } } return $parsed; } // }}} // {{{ function fileExists($file) /** * Checks if a file exists in the include path * * @param string filename * * @return bool true success and false on error * * @access public */ static function fileExists($file) { // safe_mode does notwork with is_readable() if (!@ini_get('safe_mode')) { $dirs = explode(PATH_SEPARATOR, ini_get('include_path')); foreach ($dirs as $dir) { if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) { return true; } } } else { $fp = @fopen($file, 'r', true); if (is_resource($fp)) { @fclose($fp); return true; } } return false; } // }}} } // }}} // {{{ class MDB2_Error extends PEAR_Error /** * MDB2_Error implements a class for reporting portable database error * messages. * * @package MDB2 * @category Database * @author Stig Bakken */ class MDB2_Error extends PEAR_Error { // {{{ constructor: function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null) /** * MDB2_Error constructor. * * @param mixed MDB2 error code, or string with error message. * @param int what 'error mode' to operate in * @param int what error level to use for $mode & PEAR_ERROR_TRIGGER * @param mixed additional debug info, such as the last query */ function __construct($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null, $dummy = null) { if (null === $code) { $code = MDB2_ERROR; } $this->PEAR_Error('MDB2 Error: '.MDB2::errorMessage($code), $code, $mode, $level, $debuginfo); } // }}} } // }}} // {{{ class MDB2_Driver_Common extends PEAR /** * MDB2_Driver_Common: Base class that is extended by each MDB2 driver * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Driver_Common { // {{{ Variables (Properties) /** * @var MDB2_Driver_Datatype_Common */ public $datatype; /** * @var MDB2_Extended */ public $extended; /** * @var MDB2_Driver_Function_Common */ public $function; /** * @var MDB2_Driver_Manager_Common */ public $manager; /** * @var MDB2_Driver_Native_Commonn */ public $native; /** * @var MDB2_Driver_Reverse_Common */ public $reverse; /** * index of the MDB2 object within the $GLOBALS['_MDB2_databases'] array * @var int * @access public */ public $db_index = 0; /** * DSN used for the next query * @var array * @access protected */ public $dsn = array(); /** * DSN that was used to create the current connection * @var array * @access protected */ public $connected_dsn = array(); /** * connection resource * @var mixed * @access protected */ public $connection = 0; /** * if the current opened connection is a persistent connection * @var bool * @access protected */ public $opened_persistent; /** * the name of the database for the next query * @var string * @access public */ public $database_name = ''; /** * the name of the database currently selected * @var string * @access protected */ public $connected_database_name = ''; /** * server version information * @var string * @access protected */ public $connected_server_info = ''; /** * list of all supported features of the given driver * @var array * @access public */ public $supported = array( 'sequences' => false, 'indexes' => false, 'affected_rows' => false, 'summary_functions' => false, 'order_by_text' => false, 'transactions' => false, 'savepoints' => false, 'current_id' => false, 'limit_queries' => false, 'LOBs' => false, 'replace' => false, 'sub_selects' => false, 'triggers' => false, 'auto_increment' => false, 'primary_key' => false, 'result_introspection' => false, 'prepared_statements' => false, 'identifier_quoting' => false, 'pattern_escaping' => false, 'new_link' => false, ); /** * Array of supported options that can be passed to the MDB2 instance. * * The options can be set during object creation, using * MDB2::connect(), MDB2::factory() or MDB2::singleton(). The options can * also be set after the object is created, using MDB2::setOptions() or * MDB2_Driver_Common::setOption(). * The list of available option includes: *
    *
  • $options['ssl'] -> boolean: determines if ssl should be used for connections
  • *
  • $options['field_case'] -> CASE_LOWER|CASE_UPPER: determines what case to force on field/table names
  • *
  • $options['disable_query'] -> boolean: determines if queries should be executed
  • *
  • $options['result_class'] -> string: class used for result sets
  • *
  • $options['buffered_result_class'] -> string: class used for buffered result sets
  • *
  • $options['result_wrap_class'] -> string: class used to wrap result sets into
  • *
  • $options['result_buffering'] -> boolean should results be buffered or not?
  • *
  • $options['fetch_class'] -> string: class to use when fetch mode object is used
  • *
  • $options['persistent'] -> boolean: persistent connection?
  • *
  • $options['debug'] -> integer: numeric debug level
  • *
  • $options['debug_handler'] -> string: function/method that captures debug messages
  • *
  • $options['debug_expanded_output'] -> bool: BC option to determine if more context information should be send to the debug handler
  • *
  • $options['default_text_field_length'] -> integer: default text field length to use
  • *
  • $options['lob_buffer_length'] -> integer: LOB buffer length
  • *
  • $options['log_line_break'] -> string: line-break format
  • *
  • $options['idxname_format'] -> string: pattern for index name
  • *
  • $options['seqname_format'] -> string: pattern for sequence name
  • *
  • $options['savepoint_format'] -> string: pattern for auto generated savepoint names
  • *
  • $options['statement_format'] -> string: pattern for prepared statement names
  • *
  • $options['seqcol_name'] -> string: sequence column name
  • *
  • $options['quote_identifier'] -> boolean: if identifier quoting should be done when check_option is used
  • *
  • $options['use_transactions'] -> boolean: if transaction use should be enabled
  • *
  • $options['decimal_places'] -> integer: number of decimal places to handle
  • *
  • $options['portability'] -> integer: portability constant
  • *
  • $options['modules'] -> array: short to long module name mapping for __call()
  • *
  • $options['emulate_prepared'] -> boolean: force prepared statements to be emulated
  • *
  • $options['datatype_map'] -> array: map user defined datatypes to other primitive datatypes
  • *
  • $options['datatype_map_callback'] -> array: callback function/method that should be called
  • *
  • $options['bindname_format'] -> string: regular expression pattern for named parameters
  • *
  • $options['multi_query'] -> boolean: determines if queries returning multiple result sets should be executed
  • *
  • $options['max_identifiers_length'] -> integer: max identifier length
  • *
  • $options['default_fk_action_onupdate'] -> string: default FOREIGN KEY ON UPDATE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']
  • *
  • $options['default_fk_action_ondelete'] -> string: default FOREIGN KEY ON DELETE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']
  • *
* * @var array * @access public * @see MDB2::connect() * @see MDB2::factory() * @see MDB2::singleton() * @see MDB2_Driver_Common::setOption() */ public $options = array( 'ssl' => false, 'field_case' => CASE_LOWER, 'disable_query' => false, 'result_class' => 'MDB2_Result_%s', 'buffered_result_class' => 'MDB2_BufferedResult_%s', 'result_wrap_class' => false, 'result_buffering' => true, 'fetch_class' => 'stdClass', 'persistent' => false, 'debug' => 0, 'debug_handler' => 'MDB2_defaultDebugOutput', 'debug_expanded_output' => false, 'default_text_field_length' => 4096, 'lob_buffer_length' => 8192, 'log_line_break' => "\n", 'idxname_format' => '%s_idx', 'seqname_format' => '%s_seq', 'savepoint_format' => 'MDB2_SAVEPOINT_%s', 'statement_format' => 'MDB2_STATEMENT_%1$s_%2$s', 'seqcol_name' => 'sequence', 'quote_identifier' => false, 'use_transactions' => true, 'decimal_places' => 2, 'portability' => MDB2_PORTABILITY_ALL, 'modules' => array( 'ex' => 'Extended', 'dt' => 'Datatype', 'mg' => 'Manager', 'rv' => 'Reverse', 'na' => 'Native', 'fc' => 'Function', ), 'emulate_prepared' => false, 'datatype_map' => array(), 'datatype_map_callback' => array(), 'nativetype_map_callback' => array(), 'lob_allow_url_include' => false, 'bindname_format' => '(?:\d+)|(?:[a-zA-Z][a-zA-Z0-9_]*)', 'max_identifiers_length' => 30, 'default_fk_action_onupdate' => 'RESTRICT', 'default_fk_action_ondelete' => 'RESTRICT', ); /** * string array * @var string * @access public */ public $string_quoting = array( 'start' => "'", 'end' => "'", 'escape' => false, 'escape_pattern' => false, ); /** * identifier quoting * @var array * @access public */ public $identifier_quoting = array( 'start' => '"', 'end' => '"', 'escape' => '"', ); /** * sql comments * @var array * @access protected */ public $sql_comments = array( array('start' => '--', 'end' => "\n", 'escape' => false), array('start' => '/*', 'end' => '*/', 'escape' => false), ); /** * comparision wildcards * @var array * @access protected */ protected $wildcards = array('%', '_'); /** * column alias keyword * @var string * @access protected */ public $as_keyword = ' AS '; /** * warnings * @var array * @access protected */ public $warnings = array(); /** * string with the debugging information * @var string * @access public */ public $debug_output = ''; /** * determine if there is an open transaction * @var bool * @access protected */ public $in_transaction = false; /** * the smart transaction nesting depth * @var int * @access protected */ public $nested_transaction_counter = null; /** * the first error that occured inside a nested transaction * @var MDB2_Error|bool * @access protected */ protected $has_transaction_error = false; /** * result offset used in the next query * @var int * @access public */ public $offset = 0; /** * result limit used in the next query * @var int * @access public */ public $limit = 0; /** * Database backend used in PHP (mysql, odbc etc.) * @var string * @access public */ public $phptype; /** * Database used with regards to SQL syntax etc. * @var string * @access public */ public $dbsyntax; /** * the last query sent to the driver * @var string * @access public */ public $last_query; /** * the default fetchmode used * @var int * @access public */ public $fetchmode = MDB2_FETCHMODE_ORDERED; /** * array of module instances * @var array * @access protected */ protected $modules = array(); /** * determines of the PHP4 destructor emulation has been enabled yet * @var array * @access protected */ protected $destructor_registered = true; /** * @var PEAR */ protected $pear; // }}} // {{{ constructor: function __construct() /** * Constructor */ function __construct() { end($GLOBALS['_MDB2_databases']); $db_index = key($GLOBALS['_MDB2_databases']) + 1; $GLOBALS['_MDB2_databases'][$db_index] = &$this; $this->db_index = $db_index; $this->pear = new PEAR; } // }}} // {{{ destructor: function __destruct() /** * Destructor */ function __destruct() { $this->disconnect(false); } // }}} // {{{ function free() /** * Free the internal references so that the instance can be destroyed * * @return bool true on success, false if result is invalid * * @access public */ function free() { unset($GLOBALS['_MDB2_databases'][$this->db_index]); unset($this->db_index); return MDB2_OK; } // }}} // {{{ function __toString() /** * String conversation * * @return string representation of the object * * @access public */ function __toString() { $info = get_class($this); $info.= ': (phptype = '.$this->phptype.', dbsyntax = '.$this->dbsyntax.')'; if ($this->connection) { $info.= ' [connected]'; } return $info; } // }}} // {{{ function errorInfo($error = null) /** * This method is used to collect information about an error * * @param mixed error code or resource * * @return array with MDB2 errorcode, native error code, native message * * @access public */ function errorInfo($error = null) { return array($error, null, null); } // }}} // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null) /** * This method is used to communicate an error and invoke error * callbacks etc. Basically a wrapper for PEAR::raiseError * without the message string. * * @param mixed $code integer error code, or a PEAR error object (all * other parameters are ignored if this parameter is * an object * @param int $mode error mode, see PEAR_Error docs * @param mixed $options 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 $userinfo Extra debug information. Defaults to the last * query and native error code. * @param string $method name of the method that triggered the error * @param string $dummy1 not used * @param bool $dummy2 not used * * @return PEAR_Error instance of a PEAR Error object * @access public * @see PEAR_Error */ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null, $method = null, $dummy1 = null, $dummy2 = false ) { $userinfo = "[Error message: $userinfo]\n"; // The error is yet a MDB2 error object if (MDB2::isError($code)) { // because we use the static PEAR::raiseError, our global // handler should be used if it is set if ((null === $mode) && !empty($this->_default_error_mode)) { $mode = $this->_default_error_mode; $options = $this->_default_error_options; } if (null === $userinfo) { $userinfo = $code->getUserinfo(); } $code = $code->getCode(); } elseif ($code == MDB2_ERROR_NOT_FOUND) { // extension not loaded: don't call $this->errorInfo() or the script // will die } elseif (isset($this->connection)) { if (!empty($this->last_query)) { $userinfo.= "[Last executed query: {$this->last_query}]\n"; } $native_errno = $native_msg = null; list($code, $native_errno, $native_msg) = $this->errorInfo($code); if ((null !== $native_errno) && $native_errno !== '') { $userinfo.= "[Native code: $native_errno]\n"; } if ((null !== $native_msg) && $native_msg !== '') { $userinfo.= "[Native message: ". strip_tags($native_msg) ."]\n"; } if (null !== $method) { $userinfo = $method.': '.$userinfo; } } $err = $this->pear->raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true); if ($err->getMode() !== PEAR_ERROR_RETURN && isset($this->nested_transaction_counter) && !$this->has_transaction_error) { $this->has_transaction_error = $err; } return $err; } // }}} // {{{ function resetWarnings() /** * reset the warning array * * @return void * * @access public */ function resetWarnings() { $this->warnings = array(); } // }}} // {{{ function getWarnings() /** * Get all warnings in reverse order. * This means that the last warning is the first element in the array * * @return array with warnings * * @access public * @see resetWarnings() */ function getWarnings() { return array_reverse($this->warnings); } // }}} // {{{ function setFetchMode($fetchmode, $object_class = 'stdClass') /** * Sets which fetch mode should be used by default on queries * on this connection * * @param int MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC * or MDB2_FETCHMODE_OBJECT * @param string the class name of the object to be returned * by the fetch methods when the * MDB2_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 possibility to use * and extend the 'MDB2_row' class. * * @return mixed MDB2_OK or MDB2 Error Object * * @access public * @see MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC, MDB2_FETCHMODE_OBJECT */ function setFetchMode($fetchmode, $object_class = 'stdClass') { switch ($fetchmode) { case MDB2_FETCHMODE_OBJECT: $this->options['fetch_class'] = $object_class; case MDB2_FETCHMODE_ORDERED: case MDB2_FETCHMODE_ASSOC: $this->fetchmode = $fetchmode; break; default: return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'invalid fetchmode mode', __FUNCTION__); } return MDB2_OK; } // }}} // {{{ function setOption($option, $value) /** * set the option for the db class * * @param string option name * @param mixed value for the option * * @return mixed MDB2_OK or MDB2 Error Object * * @access public */ function setOption($option, $value) { if (array_key_exists($option, $this->options)) { $this->options[$option] = $value; return MDB2_OK; } return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown option $option", __FUNCTION__); } // }}} // {{{ function getOption($option) /** * Returns the value of an option * * @param string option name * * @return mixed the option value or error object * * @access public */ function getOption($option) { if (array_key_exists($option, $this->options)) { return $this->options[$option]; } return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown option $option", __FUNCTION__); } // }}} // {{{ function debug($message, $scope = '', $is_manip = null) /** * set a debug message * * @param string message that should be appended to the debug variable * @param string usually the method name that triggered the debug call: * for example 'query', 'prepare', 'execute', 'parameters', * 'beginTransaction', 'commit', 'rollback' * @param array contains context information about the debug() call * common keys are: is_manip, time, result etc. * * @return void * * @access public */ function debug($message, $scope = '', $context = array()) { if ($this->options['debug'] && $this->options['debug_handler']) { if (!$this->options['debug_expanded_output']) { if (!empty($context['when']) && $context['when'] !== 'pre') { return null; } $context = empty($context['is_manip']) ? false : $context['is_manip']; } return call_user_func_array($this->options['debug_handler'], array(&$this, $scope, $message, $context)); } return null; } // }}} // {{{ function getDebugOutput() /** * output debug info * * @return string content of the debug_output class variable * * @access public */ function getDebugOutput() { return $this->debug_output; } // }}} // {{{ function escape($text) /** * Quotes a string so it can be safely used in a query. It will quote * the text so it can safely be used within a query. * * @param string the input string to quote * @param bool escape wildcards * * @return string quoted string * * @access public */ function escape($text, $escape_wildcards = false) { if ($escape_wildcards) { $text = $this->escapePattern($text); } $text = str_replace($this->string_quoting['end'], $this->string_quoting['escape'] . $this->string_quoting['end'], $text); return $text; } // }}} // {{{ function escapePattern($text) /** * Quotes pattern (% and _) characters in a string) * * @param string the input string to quote * * @return string quoted string * * @access public */ function escapePattern($text) { if ($this->string_quoting['escape_pattern']) { $text = str_replace($this->string_quoting['escape_pattern'], $this->string_quoting['escape_pattern'] . $this->string_quoting['escape_pattern'], $text); foreach ($this->wildcards as $wildcard) { $text = str_replace($wildcard, $this->string_quoting['escape_pattern'] . $wildcard, $text); } } return $text; } // }}} // {{{ function quoteIdentifier($str, $check_option = false) /** * Quote 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. * * NOTE: if you have table names containing periods, don't use this method * (@see bug #11906) * * 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 * + pgsql * + sqlite * * InterBase doesn't seem to be able to use delimited identifiers * via PHP 4. They work fine under PHP 5. * * @param string identifier name to be quoted * @param bool check the 'quote_identifier' option * * @return string quoted identifier string * * @access public */ function quoteIdentifier($str, $check_option = false) { if ($check_option && !$this->options['quote_identifier']) { return $str; } $str = str_replace($this->identifier_quoting['end'], $this->identifier_quoting['escape'] . $this->identifier_quoting['end'], $str); $parts = explode('.', $str); foreach (array_keys($parts) as $k) { $parts[$k] = $this->identifier_quoting['start'] . $parts[$k] . $this->identifier_quoting['end']; } return implode('.', $parts); } // }}} // {{{ function getAsKeyword() /** * Gets the string to alias column * * @return string to use when aliasing a column */ function getAsKeyword() { return $this->as_keyword; } // }}} // {{{ function getConnection() /** * Returns a native connection * * @return mixed a valid MDB2 connection object, * or a MDB2 error object on error * * @access public */ function getConnection() { $result = $this->connect(); if (MDB2::isError($result)) { return $result; } return $this->connection; } // }}} // {{{ function _fixResultArrayValues(&$row, $mode) /** * Do all necessary conversions on result arrays to fix DBMS quirks * * @param array the array to be fixed (passed by reference) * @param array bit-wise addition of the required portability modes * * @return void * * @access protected */ function _fixResultArrayValues(&$row, $mode) { switch ($mode) { case MDB2_PORTABILITY_EMPTY_TO_NULL: foreach ($row as $key => $value) { if ($value === '') { $row[$key] = null; } } break; case MDB2_PORTABILITY_RTRIM: foreach ($row as $key => $value) { if (is_string($value)) { $row[$key] = rtrim($value); } } break; case MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES: $tmp_row = array(); foreach ($row as $key => $value) { $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value; } $row = $tmp_row; break; case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL): foreach ($row as $key => $value) { if ($value === '') { $row[$key] = null; } elseif (is_string($value)) { $row[$key] = rtrim($value); } } break; case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES): $tmp_row = array(); foreach ($row as $key => $value) { if (is_string($value)) { $value = rtrim($value); } $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value; } $row = $tmp_row; break; case (MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES): $tmp_row = array(); foreach ($row as $key => $value) { if ($value === '') { $value = null; } $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value; } $row = $tmp_row; break; case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES): $tmp_row = array(); foreach ($row as $key => $value) { if ($value === '') { $value = null; } elseif (is_string($value)) { $value = rtrim($value); } $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value; } $row = $tmp_row; break; } } // }}} // {{{ function loadModule($module, $property = null, $phptype_specific = null) /** * loads a module * * @param string name of the module that should be loaded * (only used for error messages) * @param string name of the property into which the class will be loaded * @param bool if the class to load for the module is specific to the * phptype * * @return object on success a reference to the given module is returned * and on failure a PEAR error * * @access public */ function loadModule($module, $property = null, $phptype_specific = null) { if (!$property) { $property = strtolower($module); } if (!isset($this->{$property})) { $version = $phptype_specific; if ($phptype_specific !== false) { $version = true; $class_name = 'MDB2_Driver_'.$module.'_'.$this->phptype; $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php'; } if ($phptype_specific === false || (!MDB2::classExists($class_name) && !MDB2::fileExists($file_name)) ) { $version = false; $class_name = 'MDB2_'.$module; $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php'; } $err = MDB2::loadClass($class_name, $this->getOption('debug')); if (MDB2::isError($err)) { return $err; } // load module in a specific version if ($version) { if (method_exists($class_name, 'getClassName')) { $class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index); if ($class_name != $class_name_new) { $class_name = $class_name_new; $err = MDB2::loadClass($class_name, $this->getOption('debug')); if (MDB2::isError($err)) { return $err; } } } } if (!MDB2::classExists($class_name)) { $err = $this->raiseError(MDB2_ERROR_LOADMODULE, null, null, "unable to load module '$module' into property '$property'", __FUNCTION__); return $err; } $this->{$property} = new $class_name($this->db_index); $this->modules[$module] = $this->{$property}; if ($version) { // this will be used in the connect method to determine if the module // needs to be loaded with a different version if the server // version changed in between connects $this->loaded_version_modules[] = $property; } } return $this->{$property}; } // }}} // {{{ function __call($method, $params) /** * Calls a module method using the __call magic method * * @param string Method name. * @param array Arguments. * * @return mixed Returned value. */ function __call($method, $params) { $module = null; if (preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match) && isset($this->options['modules'][$match[1]]) ) { $module = $this->options['modules'][$match[1]]; $method = strtolower($match[2]).$match[3]; if (!isset($this->modules[$module]) || !is_object($this->modules[$module])) { $result = $this->loadModule($module); if (MDB2::isError($result)) { return $result; } } } else { foreach ($this->modules as $key => $foo) { if (is_object($this->modules[$key]) && method_exists($this->modules[$key], $method) ) { $module = $key; break; } } } if (null !== $module) { return call_user_func_array(array(&$this->modules[$module], $method), $params); } $class = get_class($this); $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $loc = 'in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line']; if ($method == 'isError') { trigger_error("Deprecated: $class::isError() is deprecated, use MDB2::isError() $loc", E_USER_DEPRECATED); if (!array_key_exists(0, $params)) { trigger_error("Missing argument 1 for $class::$method, called $loc", E_USER_ERROR); } return MDB2::isError($params[0]); } trigger_error("Call to undefined function: $class::$method() $loc.", E_USER_ERROR); } // }}} // {{{ function __callStatic($method, $params) /** * Calls a module method using the __callStatic magic method * * @param string Method name. * @param array Arguments. * * @return mixed Returned value. */ public static function __callStatic($method, $params) { $class = get_called_class(); $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); $loc = 'in ' . $trace[0]['file'] . ' on line ' . $trace[0]['line']; if ($method == 'isError') { trigger_error("Deprecated: $class::isError() is deprecated, use MDB2::isError() $loc", E_USER_DEPRECATED); if (!array_key_exists(0, $params)) { trigger_error("Missing argument 1 for $class::$method, called $loc", E_USER_ERROR); } return MDB2::isError($params[0]); } trigger_error("Call to undefined function: $class::$method() $loc.", E_USER_ERROR); } // }}} // {{{ function beginTransaction($savepoint = null) /** * Start a transaction or set a savepoint. * * @param string name of a savepoint to set * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function beginTransaction($savepoint = null) { $this->debug('Starting transaction', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'transactions are not supported', __FUNCTION__); } // }}} // {{{ function commit($savepoint = null) /** * Commit the database changes done during a transaction that is in * progress or release a savepoint. This function may only be called when * auto-committing is disabled, otherwise it will fail. Therefore, a new * transaction is implicitly started after committing the pending changes. * * @param string name of a savepoint to release * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function commit($savepoint = null) { $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'commiting transactions is not supported', __FUNCTION__); } // }}} // {{{ function rollback($savepoint = null) /** * Cancel any database changes done during a transaction or since a specific * savepoint that is in progress. This function may only be called when * auto-committing is disabled, otherwise it will fail. Therefore, a new * transaction is implicitly started after canceling the pending changes. * * @param string name of a savepoint to rollback to * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function rollback($savepoint = null) { $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint)); return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'rolling back transactions is not supported', __FUNCTION__); } // }}} // {{{ function inTransaction($ignore_nested = false) /** * If a transaction is currently open. * * @param bool if the nested transaction count should be ignored * @return int|bool - an integer with the nesting depth is returned if a * nested transaction is open * - true is returned for a normal open transaction * - false is returned if no transaction is open * * @access public */ function inTransaction($ignore_nested = false) { if (!$ignore_nested && isset($this->nested_transaction_counter)) { return $this->nested_transaction_counter; } return $this->in_transaction; } // }}} // {{{ function setTransactionIsolation($isolation) /** * Set the transacton isolation level. * * @param string standard isolation level * READ UNCOMMITTED (allows dirty reads) * READ COMMITTED (prevents dirty reads) * REPEATABLE READ (prevents nonrepeatable reads) * SERIALIZABLE (prevents phantom reads) * @param array some transaction options: * 'wait' => 'WAIT' | 'NO WAIT' * 'rw' => 'READ WRITE' | 'READ ONLY' * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public * @since 2.1.1 */ function setTransactionIsolation($isolation, $options = array()) { $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true)); return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'isolation level setting is not supported', __FUNCTION__); } // }}} // {{{ function beginNestedTransaction($savepoint = false) /** * Start a nested transaction. * * @return mixed MDB2_OK on success/savepoint name, a MDB2 error on failure * * @access public * @since 2.1.1 */ function beginNestedTransaction() { if ($this->in_transaction) { ++$this->nested_transaction_counter; $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter); if ($this->supports('savepoints') && $savepoint) { return $this->beginTransaction($savepoint); } return MDB2_OK; } $this->has_transaction_error = false; $result = $this->beginTransaction(); $this->nested_transaction_counter = 1; return $result; } // }}} // {{{ function completeNestedTransaction($force_rollback = false, $release = false) /** * Finish a nested transaction by rolling back if an error occured or * committing otherwise. * * @param bool if the transaction should be rolled back regardless * even if no error was set within the nested transaction * @return mixed MDB_OK on commit/counter decrementing, false on rollback * and a MDB2 error on failure * * @access public * @since 2.1.1 */ function completeNestedTransaction($force_rollback = false) { if ($this->nested_transaction_counter > 1) { $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter); if ($this->supports('savepoints') && $savepoint) { if ($force_rollback || $this->has_transaction_error) { $result = $this->rollback($savepoint); if (!MDB2::isError($result)) { $result = false; $this->has_transaction_error = false; } } else { $result = $this->commit($savepoint); } } else { $result = MDB2_OK; } --$this->nested_transaction_counter; return $result; } $this->nested_transaction_counter = null; $result = MDB2_OK; // transaction has not yet been rolled back if ($this->in_transaction) { if ($force_rollback || $this->has_transaction_error) { $result = $this->rollback(); if (!MDB2::isError($result)) { $result = false; } } else { $result = $this->commit(); } } $this->has_transaction_error = false; return $result; } // }}} // {{{ function failNestedTransaction($error = null, $immediately = false) /** * Force setting nested transaction to failed. * * @param mixed value to return in getNestededTransactionError() * @param bool if the transaction should be rolled back immediately * @return bool MDB2_OK * * @access public * @since 2.1.1 */ function failNestedTransaction($error = null, $immediately = false) { if (null !== $error) { $error = $this->has_transaction_error ? $this->has_transaction_error : true; } elseif (!$error) { $error = true; } $this->has_transaction_error = $error; if (!$immediately) { return MDB2_OK; } return $this->rollback(); } // }}} // {{{ function getNestedTransactionError() /** * The first error that occured since the transaction start. * * @return MDB2_Error|bool MDB2 error object if an error occured or false. * * @access public * @since 2.1.1 */ function getNestedTransactionError() { return $this->has_transaction_error; } // }}} // {{{ connect() /** * Connect to the database * * @return true on success, MDB2 Error Object on failure */ function connect() { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ databaseExists() /** * check if given database name is exists? * * @param string $name name of the database that should be checked * * @return mixed true/false on success, a MDB2 error on failure * @access public */ function databaseExists($name) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ setCharset($charset, $connection = null) /** * Set the charset on the current connection * * @param string charset * @param resource connection handle * * @return true on success, MDB2 Error Object on failure */ function setCharset($charset, $connection = null) { return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function disconnect($force = true) /** * Log out and disconnect from the database. * * @param boolean $force whether the disconnect should be forced even if the * connection is opened persistently * * @return mixed true on success, false if not connected and error object on error * * @access public */ function disconnect($force = true) { $this->connection = 0; $this->connected_dsn = array(); $this->connected_database_name = ''; $this->opened_persistent = null; $this->connected_server_info = ''; $this->in_transaction = null; $this->nested_transaction_counter = null; return MDB2_OK; } // }}} // {{{ function setDatabase($name) /** * Select a different database * * @param string name of the database that should be selected * * @return string name of the database previously connected to * * @access public */ function setDatabase($name) { $previous_database_name = (isset($this->database_name)) ? $this->database_name : ''; $this->database_name = $name; if (!empty($this->connected_database_name) && ($this->connected_database_name != $this->database_name)) { $this->disconnect(false); } return $previous_database_name; } // }}} // {{{ function getDatabase() /** * Get the current database * * @return string name of the database * * @access public */ function getDatabase() { return $this->database_name; } // }}} // {{{ function setDSN($dsn) /** * set the DSN * * @param mixed DSN string or array * * @return MDB2_OK * * @access public */ function setDSN($dsn) { $dsn_default = $GLOBALS['_MDB2_dsninfo_default']; $dsn = MDB2::parseDSN($dsn); if (array_key_exists('database', $dsn)) { $this->database_name = $dsn['database']; unset($dsn['database']); } $this->dsn = array_merge($dsn_default, $dsn); return $this->disconnect(false); } // }}} // {{{ function getDSN($type = 'string', $hidepw = false) /** * return the DSN as a string * * @param string format to return ("array", "string") * @param string string to hide the password with * * @return mixed DSN in the chosen type * * @access public */ function getDSN($type = 'string', $hidepw = false) { $dsn = array_merge($GLOBALS['_MDB2_dsninfo_default'], $this->dsn); $dsn['phptype'] = $this->phptype; $dsn['database'] = $this->database_name; if ($hidepw) { $dsn['password'] = $hidepw; } switch ($type) { // expand to include all possible options case 'string': $dsn = $dsn['phptype']. ($dsn['dbsyntax'] ? ('('.$dsn['dbsyntax'].')') : ''). '://'.$dsn['username'].':'. $dsn['password'].'@'.$dsn['hostspec']. ($dsn['port'] ? (':'.$dsn['port']) : ''). '/'.$dsn['database']; break; case 'array': default: break; } return $dsn; } // }}} // {{{ _isNewLinkSet() /** * Check if the 'new_link' option is set * * @return boolean * * @access protected */ function _isNewLinkSet() { return (isset($this->dsn['new_link']) && ($this->dsn['new_link'] === true || (is_string($this->dsn['new_link']) && preg_match('/^true$/i', $this->dsn['new_link'])) || (is_numeric($this->dsn['new_link']) && 0 != (int)$this->dsn['new_link']) ) ); } // }}} // {{{ function &standaloneQuery($query, $types = null, $is_manip = false) /** * execute a query as database administrator * * @param string the SQL query * @param mixed array that contains the types of the columns in * the result set * @param bool if the query is a manipulation query * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function standaloneQuery($query, $types = null, $is_manip = false) { $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $query = $this->_modifyQuery($query, $is_manip, $limit, $offset); $connection = $this->getConnection(); if (MDB2::isError($connection)) { return $connection; } $result = $this->_doQuery($query, $is_manip, $connection, false); if (MDB2::isError($result)) { return $result; } if ($is_manip) { $affected_rows = $this->_affectedRows($connection, $result); return $affected_rows; } $result = $this->_wrapResult($result, $types, true, true, $limit, $offset); return $result; } // }}} // {{{ function _modifyQuery($query, $is_manip, $limit, $offset) /** * Changes a query string for various DBMS specific reasons * * @param string query to modify * @param bool if it is a DML query * @param int limit the number of rows * @param int start reading from given offset * * @return string modified query * * @access protected */ function _modifyQuery($query, $is_manip, $limit, $offset) { return $query; } // }}} // {{{ function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null) /** * Execute a query * @param string query * @param bool if the query is a manipulation query * @param resource connection handle * @param string database name * * @return result or error object * * @access protected */ function _doQuery($query, $is_manip = false, $connection = null, $database_name = null) { $this->last_query = $query; $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre')); if ($result) { if (MDB2::isError($result)) { return $result; } $query = $result; } $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $err; } // }}} // {{{ function _affectedRows($connection, $result = null) /** * Returns the number of rows affected * * @param resource result handle * @param resource connection handle * * @return mixed MDB2 Error Object or the number of rows affected * * @access private */ function _affectedRows($connection, $result = null) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function &exec($query) /** * Execute a manipulation query to the database and return the number of affected rows * * @param string the SQL query * * @return mixed number of affected rows on success, a MDB2 error on failure * * @access public */ function exec($query) { $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $query = $this->_modifyQuery($query, true, $limit, $offset); $connection = $this->getConnection(); if (MDB2::isError($connection)) { return $connection; } $result = $this->_doQuery($query, true, $connection, $this->database_name); if (MDB2::isError($result)) { return $result; } $affectedRows = $this->_affectedRows($connection, $result); return $affectedRows; } // }}} // {{{ function &query($query, $types = null, $result_class = true, $result_wrap_class = false) /** * Send a query to the database and return any results * * @param string the SQL query * @param mixed array that contains the types of the columns in * the result set * @param mixed string which specifies which result class to use * @param mixed string which specifies which class to wrap results in * * @return mixed an MDB2_Result handle on success, a MDB2 error on failure * * @access public */ function query($query, $types = null, $result_class = true, $result_wrap_class = true) { $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $query = $this->_modifyQuery($query, false, $limit, $offset); $connection = $this->getConnection(); if (MDB2::isError($connection)) { return $connection; } $result = $this->_doQuery($query, false, $connection, $this->database_name); if (MDB2::isError($result)) { return $result; } $result = $this->_wrapResult($result, $types, $result_class, $result_wrap_class, $limit, $offset); return $result; } // }}} // {{{ function _wrapResult($result_resource, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null) /** * wrap a result set into the correct class * * @param resource result handle * @param mixed array that contains the types of the columns in * the result set * @param mixed string which specifies which result class to use * @param mixed string which specifies which class to wrap results in * @param string number of rows to select * @param string first row to select * * @return mixed an MDB2_Result, a MDB2 error on failure * * @access protected */ function _wrapResult($result_resource, $types = array(), $result_class = true, $result_wrap_class = true, $limit = null, $offset = null) { if ($types === true) { if ($this->supports('result_introspection')) { $this->loadModule('Reverse', null, true); $tableInfo = $this->reverse->tableInfo($result_resource); if (MDB2::isError($tableInfo)) { return $tableInfo; } $types = array(); $types_assoc = array(); foreach ($tableInfo as $field) { $types[] = $field['mdb2type']; $types_assoc[$field['name']] = $field['mdb2type']; } } else { $types = null; } } if ($result_class === true) { $result_class = $this->options['result_buffering'] ? $this->options['buffered_result_class'] : $this->options['result_class']; } if ($result_class) { $class_name = sprintf($result_class, $this->phptype); if (!MDB2::classExists($class_name)) { $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result class does not exist '.$class_name, __FUNCTION__); return $err; } $result = new $class_name($this, $result_resource, $limit, $offset); if (!MDB2::isResultCommon($result)) { $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result class is not extended from MDB2_Result_Common', __FUNCTION__); return $err; } if (!empty($types)) { $err = $result->setResultTypes($types); if (MDB2::isError($err)) { $result->free(); return $err; } } if (!empty($types_assoc)) { $err = $result->setResultTypes($types_assoc); if (MDB2::isError($err)) { $result->free(); return $err; } } if ($result_wrap_class === true) { $result_wrap_class = $this->options['result_wrap_class']; } if ($result_wrap_class) { if (!MDB2::classExists($result_wrap_class)) { $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'result wrap class does not exist '.$result_wrap_class, __FUNCTION__); return $err; } $result = new $result_wrap_class($result, $this->fetchmode); } return $result; } return $result_resource; } // }}} // {{{ function getServerVersion($native = false) /** * return version information about the server * * @param bool determines if the raw version string should be returned * * @return mixed array with version information or row string * * @access public */ function getServerVersion($native = false) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function setLimit($limit, $offset = null) /** * set the range of the next query * * @param string number of rows to select * @param string first row to select * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function setLimit($limit, $offset = null) { if (!$this->supports('limit_queries')) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'limit is not supported by this driver', __FUNCTION__); } $limit = (int)$limit; if ($limit < 0) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 'it was not specified a valid selected range row limit', __FUNCTION__); } $this->limit = $limit; if (null !== $offset) { $offset = (int)$offset; if ($offset < 0) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 'it was not specified a valid first selected range row', __FUNCTION__); } $this->offset = $offset; } return MDB2_OK; } // }}} // {{{ function subSelect($query, $type = false) /** * simple subselect emulation: leaves the query untouched for all RDBMS * that support subselects * * @param string the SQL query for the subselect that may only * return a column * @param string determines type of the field * * @return string the query * * @access public */ function subSelect($query, $type = false) { if ($this->supports('sub_selects') === true) { return $query; } if (!$this->supports('sub_selects')) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } $col = $this->queryCol($query, $type); if (MDB2::isError($col)) { return $col; } if (!is_array($col) || count($col) == 0) { return 'NULL'; } if ($type) { $this->loadModule('Datatype', null, true); return $this->datatype->implodeArray($col, $type); } return implode(', ', $col); } // }}} // {{{ function replace($table, $fields) /** * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT * query, except that if there is already a row in the table with the same * key field values, the old row is deleted before the new row is inserted. * * The REPLACE type of query does not make part of the SQL standards. Since * practically only MySQL and SQLite implement it natively, this type of * query isemulated through this method for other DBMS using standard types * of queries inside a transaction to assure the atomicity of the operation. * * @param string name of the table on which the REPLACE query will * be executed. * @param array associative array that describes the fields and the * values that will be inserted or updated in the specified table. The * indexes of the array are the names of all the fields of the table. * The values of the array are also associative arrays that describe * the values and other properties of the table fields. * * Here follows a list of field properties that need to be specified: * * value * Value to be assigned to the specified field. This value may be * of specified in database independent type format as this * function can perform the necessary datatype conversions. * * Default: this property is required unless the Null property is * set to 1. * * type * Name of the type of the field. Currently, all types MDB2 * are supported except for clob and blob. * * Default: no type conversion * * null * bool property that indicates that the value for this field * should be set to null. * * The default value for fields missing in INSERT queries may be * specified the definition of a table. Often, the default value * is already null, but since the REPLACE may be emulated using * an UPDATE query, make sure that all fields of the table are * listed in this function argument array. * * Default: 0 * * key * bool property that indicates that this field should be * handled as a primary key or at least as part of the compound * unique index of the table that will determine the row that will * updated if it exists or inserted a new row otherwise. * * This function will fail if no key field is specified or if the * value of a key field is set to null because fields that are * part of unique index they may not be null. * * Default: 0 * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function replace($table, $fields) { if (!$this->supports('replace')) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'replace query is not supported', __FUNCTION__); } $count = count($fields); $condition = $values = array(); for ($colnum = 0, reset($fields); $colnum < $count; next($fields), $colnum++) { $name = key($fields); if (isset($fields[$name]['null']) && $fields[$name]['null']) { $value = 'NULL'; } else { $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null; $value = $this->quote($fields[$name]['value'], $type); } $values[$name] = $value; if (isset($fields[$name]['key']) && $fields[$name]['key']) { if ($value === 'NULL') { return MDB2_Driver_Common::raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 'key value '.$name.' may not be NULL', __FUNCTION__); } $condition[] = $this->quoteIdentifier($name, true) . '=' . $value; } } if (empty($condition)) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, 'not specified which fields are keys', __FUNCTION__); } $result = null; $in_transaction = $this->in_transaction; if (!$in_transaction && MDB2::isError($result = $this->beginTransaction())) { return $result; } $connection = $this->getConnection(); if (MDB2::isError($connection)) { return $connection; } $condition = ' WHERE '.implode(' AND ', $condition); $query = 'DELETE FROM ' . $this->quoteIdentifier($table, true) . $condition; $result = $this->_doQuery($query, true, $connection); if (!MDB2::isError($result)) { $affected_rows = $this->_affectedRows($connection, $result); $insert = ''; foreach ($values as $key => $value) { $insert .= ($insert?', ':'') . $this->quoteIdentifier($key, true); } $values = implode(', ', $values); $query = 'INSERT INTO '. $this->quoteIdentifier($table, true) . "($insert) VALUES ($values)"; $result = $this->_doQuery($query, true, $connection); if (!MDB2::isError($result)) { $affected_rows += $this->_affectedRows($connection, $result);; } } if (!$in_transaction) { if (MDB2::isError($result)) { $this->rollback(); } else { $result = $this->commit(); } } if (MDB2::isError($result)) { return $result; } return $affected_rows; } // }}} // {{{ function &prepare($query, $types = null, $result_types = null, $lobs = array()) /** * Prepares a query for multiple execution with execute(). * With some database backends, this is emulated. * prepare() requires a generic query as string like * 'INSERT INTO numbers VALUES(?,?)' or * 'INSERT INTO numbers VALUES(:foo,:bar)'. * The ? and :name and are placeholders which can be set using * bindParam() and the query can be sent off using the execute() method. * The allowed format for :name can be set with the 'bindname_format' option. * * @param string the query to prepare * @param mixed array that contains the types of the placeholders * @param mixed array that contains the types of the columns in * the result set or MDB2_PREPARE_RESULT, if set to * MDB2_PREPARE_MANIP the query is handled as a manipulation query * @param mixed key (field) value (parameter) pair for all lob placeholders * * @return mixed resource handle for the prepared query on success, * a MDB2 error on failure * * @access public * @see bindParam, execute */ function prepare($query, $types = null, $result_types = null, $lobs = array()) { $is_manip = ($result_types === MDB2_PREPARE_MANIP); $offset = $this->offset; $limit = $this->limit; $this->offset = $this->limit = 0; $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre')); if ($result) { if (MDB2::isError($result)) { return $result; } $query = $result; } $placeholder_type_guess = $placeholder_type = null; $question = '?'; $colon = ':'; $positions = array(); $position = 0; while ($position < strlen($query)) { $q_position = strpos($query, $question, $position); $c_position = strpos($query, $colon, $position); if ($q_position && $c_position) { $p_position = min($q_position, $c_position); } elseif ($q_position) { $p_position = $q_position; } elseif ($c_position) { $p_position = $c_position; } else { break; } if (null === $placeholder_type) { $placeholder_type_guess = $query[$p_position]; } $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position); if (MDB2::isError($new_pos)) { return $new_pos; } if ($new_pos != $position) { $position = $new_pos; continue; //evaluate again starting from the new position } if ($query[$position] == $placeholder_type_guess) { if (null === $placeholder_type) { $placeholder_type = $query[$p_position]; $question = $colon = $placeholder_type; if (!empty($types) && is_array($types)) { if ($placeholder_type == ':') { if (is_int(key($types))) { $types_tmp = $types; $types = array(); $count = -1; } } else { $types = array_values($types); } } } if ($placeholder_type == ':') { $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s'; $parameter = preg_replace($regexp, '\\1', $query); if ($parameter === '') { $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 'named parameter name must match "bindname_format" option', __FUNCTION__); return $err; } $positions[$p_position] = $parameter; $query = substr_replace($query, '?', $position, strlen($parameter)+1); // use parameter name in type array if (isset($count) && isset($types_tmp[++$count])) { $types[$parameter] = $types_tmp[$count]; } } else { $positions[$p_position] = count($positions); } $position = $p_position + 1; } else { $position = $p_position; } } $class_name = 'MDB2_Statement_'.$this->phptype; $statement = null; $obj = new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset); $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj)); return $obj; } // }}} // {{{ function _skipDelimitedStrings($query, $position, $p_position) /** * Utility method, used by prepare() to avoid replacing placeholders within delimited strings. * Check if the placeholder is contained within a delimited string. * If so, skip it and advance the position, otherwise return the current position, * which is valid * * @param string $query * @param integer $position current string cursor position * @param integer $p_position placeholder position * * @return mixed integer $new_position on success * MDB2_Error on failure * * @access protected */ function _skipDelimitedStrings($query, $position, $p_position) { $ignores = array(); $ignores[] = $this->string_quoting; $ignores[] = $this->identifier_quoting; $ignores = array_merge($ignores, $this->sql_comments); foreach ($ignores as $ignore) { if (!empty($ignore['start'])) { if (is_int($start_quote = strpos($query, $ignore['start'], $position)) && $start_quote < $p_position) { $end_quote = $start_quote; do { if (!is_int($end_quote = strpos($query, $ignore['end'], $end_quote + 1))) { if ($ignore['end'] === "\n") { $end_quote = strlen($query) - 1; } else { $err = MDB2_Driver_Common::raiseError(MDB2_ERROR_SYNTAX, null, null, 'query with an unterminated text string specified', __FUNCTION__); return $err; } } } while ($ignore['escape'] && $end_quote-1 != $start_quote && $query[($end_quote - 1)] == $ignore['escape'] && ( $ignore['escape_pattern'] !== $ignore['escape'] || $query[($end_quote - 2)] != $ignore['escape']) ); $position = $end_quote + 1; return $position; } } } return $position; } // }}} // {{{ function quote($value, $type = null, $quote = true) /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string text string value that is intended to be converted. * @param string type to which the value should be converted to * @param bool quote * @param bool escape wildcards * * @return string text string that represents the given argument value in * a DBMS specific format. * * @access public */ function quote($value, $type = null, $quote = true, $escape_wildcards = false) { $result = $this->loadModule('Datatype', null, true); if (MDB2::isError($result)) { return $result; } return $this->datatype->quote($value, $type, $quote, $escape_wildcards); } // }}} // {{{ function getDeclaration($type, $name, $field) /** * Obtain DBMS specific SQL code portion needed to declare * of the given type * * @param string type to which the value should be converted to * @param string name the field to be declared. * @param string definition of the field * * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * * @access public */ function getDeclaration($type, $name, $field) { $result = $this->loadModule('Datatype', null, true); if (MDB2::isError($result)) { return $result; } return $this->datatype->getDeclaration($type, $name, $field); } // }}} // {{{ function compareDefinition($current, $previous) /** * Obtain an array of changes that may need to applied * * @param array new definition * @param array old definition * * @return array containing all changes that will need to be applied * * @access public */ function compareDefinition($current, $previous) { $result = $this->loadModule('Datatype', null, true); if (MDB2::isError($result)) { return $result; } return $this->datatype->compareDefinition($current, $previous); } // }}} // {{{ function supports($feature) /** * Tell whether a DB implementation or its backend extension * supports a given feature. * * @param string name of the feature (see the MDB2 class doc) * * @return bool|string if this DB implementation supports a given feature * false means no, true means native, * 'emulated' means emulated * * @access public */ function supports($feature) { if (array_key_exists($feature, $this->supported)) { return $this->supported[$feature]; } return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, "unknown support feature $feature", __FUNCTION__); } // }}} // {{{ function getSequenceName($sqn) /** * adds sequence name formatting to a sequence name * * @param string name of the sequence * * @return string formatted sequence name * * @access public */ function getSequenceName($sqn) { return sprintf($this->options['seqname_format'], preg_replace('/[^a-z0-9_\-\$.]/i', '_', $sqn)); } // }}} // {{{ function getIndexName($idx) /** * adds index name formatting to a index name * * @param string name of the index * * @return string formatted index name * * @access public */ function getIndexName($idx) { return sprintf($this->options['idxname_format'], preg_replace('/[^a-z0-9_\-\$.]/i', '_', $idx)); } // }}} // {{{ function nextID($seq_name, $ondemand = true) /** * Returns the next free id of a sequence * * @param string name of the sequence * @param bool when true missing sequences are automatic created * * @return mixed MDB2 Error Object or id * * @access public */ function nextID($seq_name, $ondemand = true) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function lastInsertID($table = null, $field = null) /** * Returns the autoincrement ID if supported or $id or fetches the current * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field) * * @param string name of the table into which a new row was inserted * @param string name of the field into which a new row was inserted * * @return mixed MDB2 Error Object or id * * @access public */ function lastInsertID($table = null, $field = null) { return MDB2_Driver_Common::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function currID($seq_name) /** * Returns the current id of a sequence * * @param string name of the sequence * * @return mixed MDB2 Error Object or id * * @access public */ function currID($seq_name) { $this->warnings[] = 'database does not support getting current sequence value, the sequence value was incremented'; return $this->nextID($seq_name); } // }}} // {{{ function queryOne($query, $type = null, $colnum = 0) /** * Execute the specified query, fetch the value from the first column of * the first row of the result set and then frees * the result set. * * @param string $query the SELECT query statement to be executed. * @param string $type optional argument that specifies the expected * datatype of the result set field, so that an eventual * conversion may be performed. The default datatype is * text, meaning that no conversion is performed * @param mixed $colnum the column number (or name) to fetch * * @return mixed MDB2_OK or field value on success, a MDB2 error on failure * * @access public */ function queryOne($query, $type = null, $colnum = 0) { $result = $this->query($query, $type); if (!MDB2::isResultCommon($result)) { return $result; } $one = $result->fetchOne($colnum); $result->free(); return $one; } // }}} // {{{ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT) /** * Execute the specified query, fetch the values from the first * row of the result set into an array and then frees * the result set. * * @param string the SELECT query statement to be executed. * @param array optional array argument that specifies a list of * expected datatypes of the result set columns, so that the eventual * conversions may be performed. The default list of datatypes is * empty, meaning that no conversion is performed. * @param int how the array data should be indexed * * @return mixed MDB2_OK or data array on success, a MDB2 error on failure * * @access public */ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT) { $result = $this->query($query, $types); if (!MDB2::isResultCommon($result)) { return $result; } $row = $result->fetchRow($fetchmode); $result->free(); return $row; } // }}} // {{{ function queryCol($query, $type = null, $colnum = 0) /** * Execute the specified query, fetch the value from the first column of * each row of the result set into an array and then frees the result set. * * @param string $query the SELECT query statement to be executed. * @param string $type optional argument that specifies the expected * datatype of the result set field, so that an eventual * conversion may be performed. The default datatype is text, * meaning that no conversion is performed * @param mixed $colnum the column number (or name) to fetch * * @return mixed MDB2_OK or data array on success, a MDB2 error on failure * @access public */ function queryCol($query, $type = null, $colnum = 0) { $result = $this->query($query, $type); if (!MDB2::isResultCommon($result)) { return $result; } $col = $result->fetchCol($colnum); $result->free(); return $col; } // }}} // {{{ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false) /** * Execute the specified query, fetch all the rows of the result set into * a two dimensional array and then frees the result set. * * @param string the SELECT query statement to be executed. * @param array optional array argument that specifies a list of * expected datatypes of the result set columns, so that the eventual * conversions may be performed. The default list of datatypes is * empty, meaning that no conversion is performed. * @param int how the array data should be indexed * @param bool if set to true, the $all will have the first * column as its first dimension * @param bool 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 bool if true, the values of the returned array is * wrapped in another array. If the same key value (in the first * column) repeats itself, the values will be appended to this array * instead of overwriting the existing values. * * @return mixed MDB2_OK or data array on success, a MDB2 error on failure * * @access public */ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false) { $result = $this->query($query, $types); if (!MDB2::isResultCommon($result)) { return $result; } $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group); $result->free(); return $all; } // }}} // {{{ function delExpect($error_code) /** * This method deletes all occurences of the specified element from * the expected error codes stack. * * @param mixed $error_code error code that should be deleted * @return mixed list of error codes that were deleted or error * * @uses PEAR::delExpect() */ public function delExpect($error_code) { return $this->pear->delExpect($error_code); } // }}} // {{{ function expectError($code) /** * This method is used to tell which errors you expect to get. * Expected errors are always returned with error mode * PEAR_ERROR_RETURN. Expected error codes are stored in a stack, * and this method pushes a new element onto it. The list of * expected errors are in effect until they are popped off the * stack with the popExpect() method. * * Note that this method can not be called statically * * @param mixed $code a single error code or an array of error codes to expect * * @return int the new depth of the "expected errors" stack * * @uses PEAR::expectError() */ public function expectError($code = '*') { return $this->pear->expectError($code); } // }}} // {{{ function getStaticProperty($class, $var) /** * If you have a class that's mostly/entirely static, and you need static * properties, you can use this method to simulate them. Eg. in your method(s) * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar'); * You MUST use a reference, or they will not persist! * * @param string $class The calling classname, to prevent clashes * @param string $var The variable to retrieve. * @return mixed A reference to the variable. If not set it will be * auto initialised to NULL. * * @uses PEAR::getStaticProperty() */ public function &getStaticProperty($class, $var) { $tmp =& $this->pear->getStaticProperty($class, $var); return $tmp; } // }}} // {{{ function popErrorHandling() /** * Pop the last error handler used * * @return bool Always true * * @see PEAR::pushErrorHandling * @uses PEAR::popErrorHandling() */ public function popErrorHandling() { return $this->pear->popErrorHandling(); } // }}} // {{{ function popExpect() /** * This method pops one element off the expected error codes * stack. * * @return array the list of error codes that were popped * * @uses PEAR::popExpect() */ public function popExpect() { return $this->pear->popExpect(); } // }}} // {{{ function pushErrorHandling($mode, $options = null) /** * Push a new error handler on top of the error handler options stack. With this * you can easily override the actual error handler for some code and restore * it later with popErrorHandling. * * @param mixed $mode (same as setErrorHandling) * @param mixed $options (same as setErrorHandling) * * @return bool Always true * * @see PEAR::setErrorHandling * @uses PEAR::pushErrorHandling() */ public function pushErrorHandling($mode, $options = null) { return $this->pear->pushErrorHandling($mode, $options); } // }}} // {{{ function registerShutdownFunc($func, $args = array()) /** * Use this function to register a shutdown method for static * classes. * * @param mixed $func The function name (or array of class/method) to call * @param mixed $args The arguments to pass to the function * @return void * * @uses PEAR::registerShutdownFunc() */ public function registerShutdownFunc($func, $args = array()) { return $this->pear->registerShutdownFunc($func, $args); } // }}} // {{{ function setErrorHandling($mode = null, $options = null) /** * Sets how errors generated by this object should be handled. * Can be invoked both in objects and statically. If called * statically, setErrorHandling sets the default behaviour for all * PEAR objects. If called in an object, setErrorHandling sets * the default behaviour for that object. * * @param int $mode * One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT, * PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE, * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION. * * @param mixed $options * When $mode is PEAR_ERROR_TRIGGER, this is the error level (one * of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR). * * When $mode is PEAR_ERROR_CALLBACK, this parameter is expected * to be the callback function or method. A callback * function is a string with the name of the function, a * callback method is an array of two elements: the element * at index 0 is the object, and the element at index 1 is * the name of the method to call in the object. * * When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is * a printf format string used when printing the error * message. * * @access public * @return void * @see PEAR_ERROR_RETURN * @see PEAR_ERROR_PRINT * @see PEAR_ERROR_TRIGGER * @see PEAR_ERROR_DIE * @see PEAR_ERROR_CALLBACK * @see PEAR_ERROR_EXCEPTION * * @since PHP 4.0.5 * @uses PEAR::setErrorHandling($mode, $options) */ public function setErrorHandling($mode = null, $options = null) { return $this->pear->setErrorHandling($mode, $options); } /** * @uses PEAR::staticPopErrorHandling() */ public function staticPopErrorHandling() { return $this->pear->staticPopErrorHandling(); } // }}} // {{{ function staticPushErrorHandling($mode, $options = null) /** * @uses PEAR::staticPushErrorHandling($mode, $options) */ public function staticPushErrorHandling($mode, $options = null) { return $this->pear->staticPushErrorHandling($mode, $options); } // }}} // {{{ function &throwError($message = null, $code = null, $userinfo = null) /** * Simpler form of raiseError with fewer options. In most cases * message, code and userinfo are enough. * * @param mixed $message a text error message or a PEAR error object * * @param int $code a numeric error code (it is up to your class * to define these if you want to use codes) * * @param string $userinfo If you need to pass along for example debug * information, this parameter is meant for that. * * @return object a PEAR error object * @see PEAR::raiseError * @uses PEAR::&throwError() */ public function &throwError($message = null, $code = null, $userinfo = null) { $tmp =& $this->pear->throwError($message, $code, $userinfo); return $tmp; } // }}} } // }}} // {{{ class MDB2_Result /** * The dummy class that all user space result classes should extend from * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Result { } // }}} // {{{ class MDB2_Result_Common extends MDB2_Result /** * The common result class for MDB2 result objects * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Result_Common extends MDB2_Result { // {{{ Variables (Properties) public $db; public $result; public $rownum = -1; public $types = array(); public $types_assoc = array(); public $values = array(); public $offset; public $offset_count = 0; public $limit; public $column_names; // }}} // {{{ constructor: function __construct($db, &$result, $limit = 0, $offset = 0) /** * Constructor */ function __construct($db, &$result, $limit = 0, $offset = 0) { $this->db = $db; $this->result = $result; $this->offset = $offset; $this->limit = max(0, $limit - 1); } // }}} // {{{ function setResultTypes($types) /** * Define the list of types to be associated with the columns of a given * result set. * * This function may be called before invoking fetchRow(), fetchOne(), * fetchCol() and fetchAll() so that the necessary data type * conversions are performed on the data to be retrieved by them. If this * function is not called, the type of all result set columns is assumed * to be text, thus leading to not perform any conversions. * * @param array variable that lists the * data types to be expected in the result set columns. If this array * contains less types than the number of columns that are returned * in the result set, the remaining columns are assumed to be of the * type text. Currently, the types clob and blob are not fully * supported. * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function setResultTypes($types) { $load = $this->db->loadModule('Datatype', null, true); if (MDB2::isError($load)) { return $load; } $types = $this->db->datatype->checkResultTypes($types); if (MDB2::isError($types)) { return $types; } foreach ($types as $key => $value) { if (is_numeric($key)) { $this->types[$key] = $value; } else { $this->types_assoc[$key] = $value; } } return MDB2_OK; } // }}} // {{{ function seek($rownum = 0) /** * Seek to a specific row in a result set * * @param int number of the row where the data can be found * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function seek($rownum = 0) { $target_rownum = $rownum - 1; if ($this->rownum > $target_rownum) { return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'seeking to previous rows not implemented', __FUNCTION__); } while ($this->rownum < $target_rownum) { $this->fetchRow(); } return MDB2_OK; } // }}} // {{{ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) /** * Fetch and return a row of data * * @param int how the array data should be indexed * @param int number of the row where the data can be found * * @return int data array on success, a MDB2 error on failure * * @access public */ function fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) { $err = MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $err; } // }}} // {{{ function fetchOne($colnum = 0) /** * fetch single column from the next row from a result set * * @param int|string the column number (or name) to fetch * @param int number of the row where the data can be found * * @return string data on success, a MDB2 error on failure * @access public */ function fetchOne($colnum = 0, $rownum = null) { $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC; $row = $this->fetchRow($fetchmode, $rownum); if (!is_array($row) || MDB2::isError($row)) { return $row; } if (!array_key_exists($colnum, $row)) { return MDB2::raiseError(MDB2_ERROR_TRUNCATED, null, null, 'column is not defined in the result set: '.$colnum, __FUNCTION__); } return $row[$colnum]; } // }}} // {{{ function fetchCol($colnum = 0) /** * Fetch and return a column from the current row pointer position * * @param int|string the column number (or name) to fetch * * @return mixed data array on success, a MDB2 error on failure * @access public */ function fetchCol($colnum = 0) { $column = array(); $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC; $row = $this->fetchRow($fetchmode); if (is_array($row)) { if (!array_key_exists($colnum, $row)) { return MDB2::raiseError(MDB2_ERROR_TRUNCATED, null, null, 'column is not defined in the result set: '.$colnum, __FUNCTION__); } do { $column[] = $row[$colnum]; } while (is_array($row = $this->fetchRow($fetchmode))); } if (MDB2::isError($row)) { return $row; } return $column; } // }}} // {{{ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false) /** * Fetch and return all rows from the current row pointer position * * @param int $fetchmode the fetch mode to use: * + MDB2_FETCHMODE_ORDERED * + MDB2_FETCHMODE_ASSOC * + MDB2_FETCHMODE_ORDERED | MDB2_FETCHMODE_FLIPPED * + MDB2_FETCHMODE_ASSOC | MDB2_FETCHMODE_FLIPPED * @param bool if set to true, the $all will have the first * column as its first dimension * @param bool 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 bool if true, the values of the returned array is * wrapped in another array. If the same key value (in the first * column) repeats itself, the values will be appended to this array * instead of overwriting the existing values. * * @return mixed data array on success, a MDB2 error on failure * * @access public * @see getAssoc() */ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false) { $all = array(); $row = $this->fetchRow($fetchmode); if (MDB2::isError($row)) { return $row; } elseif (!$row) { return $all; } $shift_array = $rekey ? false : null; if (null !== $shift_array) { if (is_object($row)) { $colnum = count(get_object_vars($row)); } else { $colnum = count($row); } if ($colnum < 2) { return MDB2::raiseError(MDB2_ERROR_TRUNCATED, null, null, 'rekey feature requires atleast 2 column', __FUNCTION__); } $shift_array = (!$force_array && $colnum == 2); } if ($rekey) { do { if (is_object($row)) { $arr = get_object_vars($row); $key = reset($arr); unset($row->{$key}); } else { if ( $fetchmode == MDB2_FETCHMODE_ASSOC || $fetchmode == MDB2_FETCHMODE_OBJECT ) { $key = reset($row); unset($row[key($row)]); } else { $key = array_shift($row); } if ($shift_array) { $row = array_shift($row); } } if ($group) { $all[$key][] = $row; } else { $all[$key] = $row; } } while (($row = $this->fetchRow($fetchmode))); } elseif ($fetchmode == MDB2_FETCHMODE_FLIPPED) { do { foreach ($row as $key => $val) { $all[$key][] = $val; } } while (($row = $this->fetchRow($fetchmode))); } else { do { $all[] = $row; } while (($row = $this->fetchRow($fetchmode))); } return $all; } // }}} // {{{ function rowCount() /** * Returns the actual row number that was last fetched (count from 0) * @return int * * @access public */ function rowCount() { return $this->rownum + 1; } // }}} // {{{ function numRows() /** * Returns the number of rows in a result object * * @return mixed MDB2 Error Object or the number of rows * * @access public */ function numRows() { return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function nextResult() /** * Move the internal result pointer to the next available result * * @return true on success, false if there is no more result set or an error object on failure * * @access public */ function nextResult() { return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function getColumnNames() /** * Retrieve the names of columns returned by the DBMS in a query result or * from the cache. * * @param bool If set to true the values are the column names, * otherwise the names of the columns are the keys. * @return mixed Array variable that holds the names of columns or an * MDB2 error on failure. * Some DBMS may not return any columns when the result set * does not contain any rows. * * @access public */ function getColumnNames($flip = false) { if (!isset($this->column_names)) { $result = $this->_getColumnNames(); if (MDB2::isError($result)) { return $result; } $this->column_names = $result; } if ($flip) { return array_flip($this->column_names); } return $this->column_names; } // }}} // {{{ function _getColumnNames() /** * Retrieve the names of columns returned by the DBMS in a query result. * * @return mixed Array variable that holds the names of columns as keys * or an MDB2 error on failure. * Some DBMS may not return any columns when the result set * does not contain any rows. * * @access private */ function _getColumnNames() { return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function numCols() /** * Count the number of columns returned by the DBMS in a query result. * * @return mixed integer value with the number of columns, a MDB2 error * on failure * * @access public */ function numCols() { return MDB2::raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ function getResource() /** * return the resource associated with the result object * * @return resource * * @access public */ function getResource() { return $this->result; } // }}} // {{{ function bindColumn($column, &$value, $type = null) /** * Set bind variable to a column. * * @param int column number or name * @param mixed variable reference * @param string specifies the type of the field * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function bindColumn($column, &$value, $type = null) { if (!is_numeric($column)) { $column_names = $this->getColumnNames(); if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { if ($this->db->options['field_case'] == CASE_LOWER) { $column = strtolower($column); } else { $column = strtoupper($column); } } $column = $column_names[$column]; } $this->values[$column] =& $value; if (null !== $type) { $this->types[$column] = $type; } return MDB2_OK; } // }}} // {{{ function _assignBindColumns($row) /** * Bind a variable to a value in the result row. * * @param array row data * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access private */ function _assignBindColumns($row) { $row = array_values($row); foreach ($row as $column => $value) { if (array_key_exists($column, $this->values)) { $this->values[$column] = $value; } } return MDB2_OK; } // }}} // {{{ function free() /** * Free the internal resources associated with result. * * @return bool true on success, false if result is invalid * * @access public */ function free() { $this->result = false; return MDB2_OK; } // }}} } // }}} // {{{ class MDB2_Row /** * The simple class that accepts row data as an array * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Row { // {{{ constructor: function __construct(&$row) /** * constructor * * @param resource row data as array */ function __construct(&$row) { foreach ($row as $key => $value) { $this->$key = &$row[$key]; } } // }}} } // }}} // {{{ class MDB2_Statement_Common /** * The common statement class for MDB2 statement objects * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Statement_Common { // {{{ Variables (Properties) var $db; var $statement; var $query; var $result_types; var $types; var $values = array(); var $limit; var $offset; var $is_manip; // }}} // {{{ constructor: function __construct($db, $statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) /** * Constructor */ function __construct($db, $statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null) { $this->db = $db; $this->statement = $statement; $this->positions = $positions; $this->query = $query; $this->types = (array)$types; $this->result_types = (array)$result_types; $this->limit = $limit; $this->is_manip = $is_manip; $this->offset = $offset; } // }}} // {{{ function bindValue($parameter, &$value, $type = null) /** * Set the value of a parameter of a prepared query. * * @param int the order number of the parameter in the query * statement. The order number of the first parameter is 1. * @param mixed value that is meant to be assigned to specified * parameter. The type of the value depends on the $type argument. * @param string specifies the type of the field * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function bindValue($parameter, $value, $type = null) { if (!is_numeric($parameter)) { if (strpos($parameter, ':') === 0) { $parameter = substr($parameter, 1); } } if (!in_array($parameter, $this->positions)) { return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); } $this->values[$parameter] = $value; if (null !== $type) { $this->types[$parameter] = $type; } return MDB2_OK; } // }}} // {{{ function bindValueArray($values, $types = null) /** * Set the values of multiple a parameter of a prepared query in bulk. * * @param array specifies all necessary information * for bindValue() the array elements must use keys corresponding to * the number of the position of the parameter. * @param array specifies the types of the fields * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public * @see bindParam() */ function bindValueArray($values, $types = null) { $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null); $parameters = array_keys($values); $this->db->pushErrorHandling(PEAR_ERROR_RETURN); $this->db->expectError(MDB2_ERROR_NOT_FOUND); foreach ($parameters as $key => $parameter) { $err = $this->bindValue($parameter, $values[$parameter], $types[$key]); if (MDB2::isError($err)) { if ($err->getCode() == MDB2_ERROR_NOT_FOUND) { //ignore (extra value for missing placeholder) continue; } $this->db->popExpect(); $this->db->popErrorHandling(); return $err; } } $this->db->popExpect(); $this->db->popErrorHandling(); return MDB2_OK; } // }}} // {{{ function bindParam($parameter, &$value, $type = null) /** * Bind a variable to a parameter of a prepared query. * * @param int the order number of the parameter in the query * statement. The order number of the first parameter is 1. * @param mixed variable that is meant to be bound to specified * parameter. The type of the value depends on the $type argument. * @param string specifies the type of the field * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function bindParam($parameter, &$value, $type = null) { if (!is_numeric($parameter)) { if (strpos($parameter, ':') === 0) { $parameter = substr($parameter, 1); } } if (!in_array($parameter, $this->positions)) { return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); } $this->values[$parameter] =& $value; if (null !== $type) { $this->types[$parameter] = $type; } return MDB2_OK; } // }}} // {{{ function bindParamArray(&$values, $types = null) /** * Bind the variables of multiple a parameter of a prepared query in bulk. * * @param array specifies all necessary information * for bindParam() the array elements must use keys corresponding to * the number of the position of the parameter. * @param array specifies the types of the fields * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public * @see bindParam() */ function bindParamArray(&$values, $types = null) { $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null); $parameters = array_keys($values); foreach ($parameters as $key => $parameter) { $err = $this->bindParam($parameter, $values[$parameter], $types[$key]); if (MDB2::isError($err)) { return $err; } } return MDB2_OK; } // }}} // {{{ function &execute($values = null, $result_class = true, $result_wrap_class = false) /** * Execute a prepared query statement. * * @param array specifies all necessary information * for bindParam() the array elements must use keys corresponding * to the number of the position of the parameter. * @param mixed specifies which result class to use * @param mixed specifies which class to wrap results in * * @return mixed MDB2_Result or integer (affected rows) on success, * a MDB2 error on failure * @access public */ function execute($values = null, $result_class = true, $result_wrap_class = false) { if (null === $this->positions) { return MDB2::raiseError(MDB2_ERROR, null, null, 'Prepared statement has already been freed', __FUNCTION__); } $values = (array)$values; if (!empty($values)) { $err = $this->bindValueArray($values); if (MDB2::isError($err)) { return MDB2::raiseError(MDB2_ERROR, null, null, 'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__); } } $result = $this->_execute($result_class, $result_wrap_class); return $result; } // }}} // {{{ function _execute($result_class = true, $result_wrap_class = false) /** * Execute a prepared query statement helper method. * * @param mixed specifies which result class to use * @param mixed specifies which class to wrap results in * * @return mixed MDB2_Result or integer (affected rows) on success, * a MDB2 error on failure * @access private */ function _execute($result_class = true, $result_wrap_class = false) { $this->last_query = $this->query; $query = ''; $last_position = 0; foreach ($this->positions as $current_position => $parameter) { if (!array_key_exists($parameter, $this->values)) { return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__); } $value = $this->values[$parameter]; $query.= substr($this->query, $last_position, $current_position - $last_position); if (!isset($value)) { $value_quoted = 'NULL'; } else { $type = !empty($this->types[$parameter]) ? $this->types[$parameter] : null; $value_quoted = $this->db->quote($value, $type); if (MDB2::isError($value_quoted)) { return $value_quoted; } } $query.= $value_quoted; $last_position = $current_position + 1; } $query.= substr($this->query, $last_position); $this->db->offset = $this->offset; $this->db->limit = $this->limit; if ($this->is_manip) { $result = $this->db->exec($query); } else { $result = $this->db->query($query, $this->result_types, $result_class, $result_wrap_class); } return $result; } // }}} // {{{ function free() /** * Release resources allocated for the specified prepared query. * * @return mixed MDB2_OK on success, a MDB2 error on failure * * @access public */ function free() { if (null === $this->positions) { return MDB2::raiseError(MDB2_ERROR, null, null, 'Prepared statement has already been freed', __FUNCTION__); } $this->statement = null; $this->positions = null; $this->query = null; $this->types = null; $this->result_types = null; $this->limit = null; $this->is_manip = null; $this->offset = null; $this->values = null; return MDB2_OK; } // }}} } // }}} // {{{ class MDB2_Module_Common /** * The common modules class for MDB2 module objects * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Module_Common { // {{{ Variables (Properties) /** * contains the key to the global MDB2 instance array of the associated * MDB2 instance * * @var int * @access protected */ protected $db_index; // }}} // {{{ constructor: function __construct($db_index) /** * Constructor */ function __construct($db_index) { $this->db_index = $db_index; } // }}} // {{{ function getDBInstance() /** * Get the instance of MDB2 associated with the module instance * * @return object MDB2 instance or a MDB2 error on failure * * @access public */ function getDBInstance() { if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) { $result = $GLOBALS['_MDB2_databases'][$this->db_index]; } else { $result = MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'could not find MDB2 instance'); } return $result; } // }}} } // }}} // {{{ function MDB2_closeOpenTransactions() /** * Close any open transactions form persistent connections * * @return void * * @access public */ function MDB2_closeOpenTransactions() { reset($GLOBALS['_MDB2_databases']); while (next($GLOBALS['_MDB2_databases'])) { $key = key($GLOBALS['_MDB2_databases']); if ($GLOBALS['_MDB2_databases'][$key]->opened_persistent && $GLOBALS['_MDB2_databases'][$key]->in_transaction ) { $GLOBALS['_MDB2_databases'][$key]->rollback(); } } } // }}} // {{{ function MDB2_defaultDebugOutput(&$db, $scope, $message, $is_manip = null) /** * default debug output handler * * @param object reference to an MDB2 database object * @param string usually the method name that triggered the debug call: * for example 'query', 'prepare', 'execute', 'parameters', * 'beginTransaction', 'commit', 'rollback' * @param string message that should be appended to the debug variable * @param array contains context information about the debug() call * common keys are: is_manip, time, result etc. * * @return void|string optionally return a modified message, this allows * rewriting a query before being issued or prepared * * @access public */ function MDB2_defaultDebugOutput(&$db, $scope, $message, $context = array()) { $db->debug_output.= $scope.'('.$db->db_index.'): '; $db->debug_output.= $message.$db->getOption('log_line_break'); return $message; } // }}} ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/000077500000000000000000000000001213666021100154135ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Date.php000066400000000000000000000145651213666021100170140ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: Date.php 327316 2012-08-27 15:17:02Z danielc $ // /** * @package MDB2 * @category Database * @author Lukas Smith */ /** * Several methods to convert the MDB2 native timestamp format (ISO based) * to and from data structures that are convenient to worth with in side of php. * For more complex date arithmetic please take a look at the Date package in PEAR * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Date { // {{{ mdbNow() /** * return the current datetime * * @return string current datetime in the MDB2 format * @access public */ public static function mdbNow() { return date('Y-m-d H:i:s'); } // }}} // {{{ mdbToday() /** * return the current date * * @return string current date in the MDB2 format * @access public */ public static function mdbToday() { return date('Y-m-d'); } // }}} // {{{ mdbTime() /** * return the current time * * @return string current time in the MDB2 format * @access public */ public static function mdbTime() { return date('H:i:s'); } // }}} // {{{ date2Mdbstamp() /** * convert a date into a MDB2 timestamp * * @param int hour of the date * @param int minute of the date * @param int second of the date * @param int month of the date * @param int day of the date * @param int year of the date * * @return string a valid MDB2 timestamp * @access public */ public static function date2Mdbstamp($hour = null, $minute = null, $second = null, $month = null, $day = null, $year = null) { return MDB2_Date::unix2Mdbstamp(mktime($hour, $minute, $second, $month, $day, $year, -1)); } // }}} // {{{ unix2Mdbstamp() /** * convert a unix timestamp into a MDB2 timestamp * * @param int a valid unix timestamp * * @return string a valid MDB2 timestamp * @access public */ public static function unix2Mdbstamp($unix_timestamp) { return date('Y-m-d H:i:s', $unix_timestamp); } // }}} // {{{ mdbstamp2Unix() /** * convert a MDB2 timestamp into a unix timestamp * * @param int a valid MDB2 timestamp * @return string unix timestamp with the time stored in the MDB2 format * * @access public */ public static function mdbstamp2Unix($mdb_timestamp) { $arr = MDB2_Date::mdbstamp2Date($mdb_timestamp); return mktime($arr['hour'], $arr['minute'], $arr['second'], $arr['month'], $arr['day'], $arr['year'], -1); } // }}} // {{{ mdbstamp2Date() /** * convert a MDB2 timestamp into an array containing all * values necessary to pass to php's date() function * * @param int a valid MDB2 timestamp * * @return array with the time split * @access public */ public static function mdbstamp2Date($mdb_timestamp) { list($arr['year'], $arr['month'], $arr['day'], $arr['hour'], $arr['minute'], $arr['second']) = sscanf($mdb_timestamp, "%04u-%02u-%02u %02u:%02u:%02u"); return $arr; } // }}} } ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/000077500000000000000000000000001213666021100166465ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Datatype/000077500000000000000000000000001213666021100204215ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Datatype/Common.php000066400000000000000000001755531213666021100224020ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: Common.php 328137 2012-10-25 02:26:35Z danielc $ require_once 'MDB2/LOB.php'; /** * @package MDB2 * @category Database * @author Lukas Smith */ /** * MDB2_Driver_Common: Base class that is extended by each MDB2 driver * * To load this module in the MDB2 object: * $mdb->loadModule('Datatype'); * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Driver_Datatype_Common extends MDB2_Module_Common { var $valid_default_values = array( 'text' => '', 'boolean' => true, 'integer' => 0, 'decimal' => 0.0, 'float' => 0.0, 'timestamp' => '1970-01-01 00:00:00', 'time' => '00:00:00', 'date' => '1970-01-01', 'clob' => '', 'blob' => '', ); /** * contains all LOB objects created with this MDB2 instance * @var array * @access protected */ var $lobs = array(); // }}} // {{{ getValidTypes() /** * Get the list of valid types * * This function returns an array of valid types as keys with the values * being possible default values for all native datatypes and mapped types * for custom datatypes. * * @return mixed array on success, a MDB2 error on failure * @access public */ function getValidTypes() { $types = $this->valid_default_values; $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!empty($db->options['datatype_map'])) { foreach ($db->options['datatype_map'] as $type => $mapped_type) { if (array_key_exists($mapped_type, $types)) { $types[$type] = $types[$mapped_type]; } elseif (!empty($db->options['datatype_map_callback'][$type])) { $parameter = array('type' => $type, 'mapped_type' => $mapped_type); $default = call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter)); $types[$type] = $default; } } } return $types; } // }}} // {{{ checkResultTypes() /** * Define the list of types to be associated with the columns of a given * result set. * * This function may be called before invoking fetchRow(), fetchOne() * fetchCole() and fetchAll() so that the necessary data type * conversions are performed on the data to be retrieved by them. If this * function is not called, the type of all result set columns is assumed * to be text, thus leading to not perform any conversions. * * @param array $types array variable that lists the * data types to be expected in the result set columns. If this array * contains less types than the number of columns that are returned * in the result set, the remaining columns are assumed to be of the * type text. Currently, the types clob and blob are not fully * supported. * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function checkResultTypes($types) { $types = is_array($types) ? $types : array($types); foreach ($types as $key => $type) { if (!isset($this->valid_default_values[$type])) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (empty($db->options['datatype_map'][$type])) { return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, $type.' for '.$key.' is not a supported column type', __FUNCTION__); } } } return $types; } // }}} // {{{ _baseConvertResult() /** * General type conversion method * * @param mixed $value reference to a value to be converted * @param string $type specifies which type to convert to * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text * @return object an MDB2 error on failure * @access protected */ function _baseConvertResult($value, $type, $rtrim = true) { switch ($type) { case 'text': if ($rtrim) { $value = rtrim($value); } return $value; case 'integer': return intval($value); case 'boolean': return !empty($value); case 'decimal': return $value; case 'float': return doubleval($value); case 'date': return $value; case 'time': return $value; case 'timestamp': return $value; case 'clob': case 'blob': $this->lobs[] = array( 'buffer' => null, 'position' => 0, 'lob_index' => null, 'endOfLOB' => false, 'resource' => $value, 'value' => null, 'loaded' => false, ); end($this->lobs); $lob_index = key($this->lobs); $this->lobs[$lob_index]['lob_index'] = $lob_index; return fopen('MDB2LOB://'.$lob_index.'@'.$this->db_index, 'r+'); } $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_INVALID, null, null, 'attempt to convert result value to an unknown type :' . $type, __FUNCTION__); } // }}} // {{{ convertResult() /** * Convert a value to a RDBMS indipendent MDB2 type * * @param mixed $value value to be converted * @param string $type specifies which type to convert to * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text * @return mixed converted value * @access public */ function convertResult($value, $type, $rtrim = true) { if (null === $value) { return null; } $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!empty($db->options['datatype_map'][$type])) { $type = $db->options['datatype_map'][$type]; if (!empty($db->options['datatype_map_callback'][$type])) { $parameter = array('type' => $type, 'value' => $value, 'rtrim' => $rtrim); return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter)); } } return $this->_baseConvertResult($value, $type, $rtrim); } // }}} // {{{ convertResultRow() /** * Convert a result row * * @param array $types * @param array $row specifies the types to convert to * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text * @return mixed MDB2_OK on success, an MDB2 error on failure * @access public */ function convertResultRow($types, $row, $rtrim = true) { //$types = $this->_sortResultFieldTypes(array_keys($row), $types); $keys = array_keys($row); if (is_int($keys[0])) { $types = $this->_sortResultFieldTypes($keys, $types); } foreach ($row as $key => $value) { if (empty($types[$key])) { continue; } $value = $this->convertResult($row[$key], $types[$key], $rtrim); if (MDB2::isError($value)) { return $value; } $row[$key] = $value; } return $row; } // }}} // {{{ _sortResultFieldTypes() /** * convert a result row * * @param array $types * @param array $row specifies the types to convert to * @param bool $rtrim if to rtrim text values or not * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function _sortResultFieldTypes($columns, $types) { $n_cols = count($columns); $n_types = count($types); if ($n_cols > $n_types) { for ($i= $n_cols - $n_types; $i >= 0; $i--) { $types[] = null; } } $sorted_types = array(); foreach ($columns as $col) { $sorted_types[$col] = null; } foreach ($types as $name => $type) { if (array_key_exists($name, $sorted_types)) { $sorted_types[$name] = $type; unset($types[$name]); } } // if there are left types in the array, fill the null values of the // sorted array with them, in order. if (count($types)) { reset($types); foreach (array_keys($sorted_types) as $k) { if (null === $sorted_types[$k]) { $sorted_types[$k] = current($types); next($types); } } } return $sorted_types; } // }}} // {{{ getDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare * of the given type * * @param string $type type to which the value should be converted to * @param string $name name the field to be declared. * @param string $field definition of the field * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access public */ function getDeclaration($type, $name, $field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!empty($db->options['datatype_map'][$type])) { $type = $db->options['datatype_map'][$type]; if (!empty($db->options['datatype_map_callback'][$type])) { $parameter = array('type' => $type, 'name' => $name, 'field' => $field); return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter)); } $field['type'] = $type; } if (!method_exists($this, "_get{$type}Declaration")) { return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null, 'type not defined: '.$type, __FUNCTION__); } return $this->{"_get{$type}Declaration"}($name, $field); } // }}} // {{{ getTypeDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare an text type * field to be used in statements like CREATE TABLE. * * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * length * Integer value that determines the maximum length of the text * field. If this argument is missing the field should be * declared to have the longest length allowed by the DBMS. * * default * Text value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access public */ function getTypeDeclaration($field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } switch ($field['type']) { case 'text': $length = !empty($field['length']) ? $field['length'] : $db->options['default_text_field_length']; $fixed = !empty($field['fixed']) ? $field['fixed'] : false; return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR('.$db->options['default_text_field_length'].')') : ($length ? 'VARCHAR('.$length.')' : 'TEXT'); case 'clob': return 'TEXT'; case 'blob': return 'TEXT'; case 'integer': return 'INT'; case 'boolean': return 'INT'; case 'date': return 'CHAR ('.strlen('YYYY-MM-DD').')'; case 'time': return 'CHAR ('.strlen('HH:MM:SS').')'; case 'timestamp': return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')'; case 'float': return 'TEXT'; case 'decimal': return 'TEXT'; } return ''; } // }}} // {{{ _getDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare a generic type * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * length * Integer value that determines the maximum length of the text * field. If this argument is missing the field should be * declared to have the longest length allowed by the DBMS. * * default * Text value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * charset * Text value with the default CHARACTER SET for this field. * collation * Text value with the default COLLATION for this field. * @return string DBMS specific SQL code portion that should be used to * declare the specified field, or a MDB2_Error on failure * @access protected */ function _getDeclaration($name, $field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); $declaration_options = $db->datatype->_getDeclarationOptions($field); if (MDB2::isError($declaration_options)) { return $declaration_options; } return $name.' '.$this->getTypeDeclaration($field).$declaration_options; } // }}} // {{{ _getDeclarationOptions() /** * Obtain DBMS specific SQL code portion needed to declare a generic type * field to be used in statement like CREATE TABLE, without the field name * and type values (ie. just the character set, default value, if the * field is permitted to be NULL or not, and the collation options). * * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * default * Text value to be used as default for this field. * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * charset * Text value with the default CHARACTER SET for this field. * collation * Text value with the default COLLATION for this field. * @return string DBMS specific SQL code portion that should be used to * declare the specified field's options. * @access protected */ function _getDeclarationOptions($field) { $charset = empty($field['charset']) ? '' : ' '.$this->_getCharsetFieldDeclaration($field['charset']); $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; $default = ''; if (array_key_exists('default', $field)) { if ($field['default'] === '') { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $valid_default_values = $this->getValidTypes(); $field['default'] = $valid_default_values[$field['type']]; if ($field['default'] === '' && ($db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)) { $field['default'] = ' '; } } if (null !== $field['default']) { $default = ' DEFAULT ' . $this->quote($field['default'], $field['type']); } } $collation = empty($field['collation']) ? '' : ' '.$this->_getCollationFieldDeclaration($field['collation']); return $charset.$default.$notnull.$collation; } // }}} // {{{ _getCharsetFieldDeclaration() /** * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET * of a field declaration to be used in statements like CREATE TABLE. * * @param string $charset name of the charset * @return string DBMS specific SQL code portion needed to set the CHARACTER SET * of a field declaration. */ function _getCharsetFieldDeclaration($charset) { return ''; } // }}} // {{{ _getCollationFieldDeclaration() /** * Obtain DBMS specific SQL code portion needed to set the COLLATION * of a field declaration to be used in statements like CREATE TABLE. * * @param string $collation name of the collation * @return string DBMS specific SQL code portion needed to set the COLLATION * of a field declaration. */ function _getCollationFieldDeclaration($collation) { return ''; } // }}} // {{{ _getIntegerDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare an integer type * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * unsigned * Boolean flag that indicates whether the field should be * declared as unsigned integer if possible. * * default * Integer value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getIntegerDeclaration($name, $field) { if (!empty($field['unsigned'])) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer"; } return $this->_getDeclaration($name, $field); } // }}} // {{{ _getTextDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare an text type * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * length * Integer value that determines the maximum length of the text * field. If this argument is missing the field should be * declared to have the longest length allowed by the DBMS. * * default * Text value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getTextDeclaration($name, $field) { return $this->_getDeclaration($name, $field); } // }}} // {{{ _getCLOBDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare an character * large object type field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * length * Integer value that determines the maximum length of the large * object field. If this argument is missing the field should be * declared to have the longest length allowed by the DBMS. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access public */ function _getCLOBDeclaration($name, $field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; $name = $db->quoteIdentifier($name, true); return $name.' '.$this->getTypeDeclaration($field).$notnull; } // }}} // {{{ _getBLOBDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare an binary large * object type field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * length * Integer value that determines the maximum length of the large * object field. If this argument is missing the field should be * declared to have the longest length allowed by the DBMS. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getBLOBDeclaration($name, $field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $notnull = empty($field['notnull']) ? '' : ' NOT NULL'; $name = $db->quoteIdentifier($name, true); return $name.' '.$this->getTypeDeclaration($field).$notnull; } // }}} // {{{ _getBooleanDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare a boolean type * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * default * Boolean value to be used as default for this field. * * notnullL * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getBooleanDeclaration($name, $field) { return $this->_getDeclaration($name, $field); } // }}} // {{{ _getDateDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare a date type * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * default * Date value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getDateDeclaration($name, $field) { return $this->_getDeclaration($name, $field); } // }}} // {{{ _getTimestampDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare a timestamp * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * default * Timestamp value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getTimestampDeclaration($name, $field) { return $this->_getDeclaration($name, $field); } // }}} // {{{ _getTimeDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare a time * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * default * Time value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getTimeDeclaration($name, $field) { return $this->_getDeclaration($name, $field); } // }}} // {{{ _getFloatDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare a float type * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * default * Float value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getFloatDeclaration($name, $field) { return $this->_getDeclaration($name, $field); } // }}} // {{{ _getDecimalDeclaration() /** * Obtain DBMS specific SQL code portion needed to declare a decimal type * field to be used in statements like CREATE TABLE. * * @param string $name name the field to be declared. * @param array $field associative array with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * default * Decimal value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * @return string DBMS specific SQL code portion that should be used to * declare the specified field. * @access protected */ function _getDecimalDeclaration($name, $field) { return $this->_getDeclaration($name, $field); } // }}} // {{{ compareDefinition() /** * Obtain an array of changes that may need to applied * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access public */ function compareDefinition($current, $previous) { $type = !empty($current['type']) ? $current['type'] : null; if (!method_exists($this, "_compare{$type}Definition")) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!empty($db->options['datatype_map_callback'][$type])) { $parameter = array('current' => $current, 'previous' => $previous); $change = call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter)); return $change; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'type "'.$current['type'].'" is not yet supported', __FUNCTION__); } if (empty($previous['type']) || $previous['type'] != $type) { return $current; } $change = $this->{"_compare{$type}Definition"}($current, $previous); if ($previous['type'] != $type) { $change['type'] = true; } $previous_notnull = !empty($previous['notnull']) ? $previous['notnull'] : false; $notnull = !empty($current['notnull']) ? $current['notnull'] : false; if ($previous_notnull != $notnull) { $change['notnull'] = true; } $previous_default = array_key_exists('default', $previous) ? $previous['default'] : null; $default = array_key_exists('default', $current) ? $current['default'] : null; if ($previous_default !== $default) { $change['default'] = true; } return $change; } // }}} // {{{ _compareIntegerDefinition() /** * Obtain an array of changes that may need to applied to an integer field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareIntegerDefinition($current, $previous) { $change = array(); $previous_length = !empty($previous['length']) ? $previous['length'] : 4; $length = !empty($current['length']) ? $current['length'] : 4; if ($previous_length != $length) { $change['length'] = $length; } $previous_unsigned = !empty($previous['unsigned']) ? $previous['unsigned'] : false; $unsigned = !empty($current['unsigned']) ? $current['unsigned'] : false; if ($previous_unsigned != $unsigned) { $change['unsigned'] = true; } $previous_autoincrement = !empty($previous['autoincrement']) ? $previous['autoincrement'] : false; $autoincrement = !empty($current['autoincrement']) ? $current['autoincrement'] : false; if ($previous_autoincrement != $autoincrement) { $change['autoincrement'] = true; } return $change; } // }}} // {{{ _compareTextDefinition() /** * Obtain an array of changes that may need to applied to an text field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareTextDefinition($current, $previous) { $change = array(); $previous_length = !empty($previous['length']) ? $previous['length'] : 0; $length = !empty($current['length']) ? $current['length'] : 0; if ($previous_length != $length) { $change['length'] = true; } $previous_fixed = !empty($previous['fixed']) ? $previous['fixed'] : 0; $fixed = !empty($current['fixed']) ? $current['fixed'] : 0; if ($previous_fixed != $fixed) { $change['fixed'] = true; } return $change; } // }}} // {{{ _compareCLOBDefinition() /** * Obtain an array of changes that may need to applied to an CLOB field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareCLOBDefinition($current, $previous) { return $this->_compareTextDefinition($current, $previous); } // }}} // {{{ _compareBLOBDefinition() /** * Obtain an array of changes that may need to applied to an BLOB field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareBLOBDefinition($current, $previous) { return $this->_compareTextDefinition($current, $previous); } // }}} // {{{ _compareDateDefinition() /** * Obtain an array of changes that may need to applied to an date field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareDateDefinition($current, $previous) { return array(); } // }}} // {{{ _compareTimeDefinition() /** * Obtain an array of changes that may need to applied to an time field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareTimeDefinition($current, $previous) { return array(); } // }}} // {{{ _compareTimestampDefinition() /** * Obtain an array of changes that may need to applied to an timestamp field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareTimestampDefinition($current, $previous) { return array(); } // }}} // {{{ _compareBooleanDefinition() /** * Obtain an array of changes that may need to applied to an boolean field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareBooleanDefinition($current, $previous) { return array(); } // }}} // {{{ _compareFloatDefinition() /** * Obtain an array of changes that may need to applied to an float field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareFloatDefinition($current, $previous) { return array(); } // }}} // {{{ _compareDecimalDefinition() /** * Obtain an array of changes that may need to applied to an decimal field * * @param array $current new definition * @param array $previous old definition * @return array containing all changes that will need to be applied * @access protected */ function _compareDecimalDefinition($current, $previous) { return array(); } // }}} // {{{ quote() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param string $type type to which the value should be converted to * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access public */ function quote($value, $type = null, $quote = true, $escape_wildcards = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if ((null === $value) || ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL) ) { if (!$quote) { return null; } return 'NULL'; } if (null === $type) { switch (gettype($value)) { case 'integer': $type = 'integer'; break; case 'double': // todo: default to decimal as float is quite unusual // $type = 'float'; $type = 'decimal'; break; case 'boolean': $type = 'boolean'; break; case 'array': $value = serialize($value); case 'object': $type = 'text'; break; default: if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $value)) { $type = 'timestamp'; } elseif (preg_match('/^\d{2}:\d{2}$/', $value)) { $type = 'time'; } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) { $type = 'date'; } else { $type = 'text'; } break; } } elseif (!empty($db->options['datatype_map'][$type])) { $type = $db->options['datatype_map'][$type]; if (!empty($db->options['datatype_map_callback'][$type])) { $parameter = array('type' => $type, 'value' => $value, 'quote' => $quote, 'escape_wildcards' => $escape_wildcards); return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter)); } } if (!method_exists($this, "_quote{$type}")) { return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'type not defined: '.$type, __FUNCTION__); } $value = $this->{"_quote{$type}"}($value, $quote, $escape_wildcards); if ($quote && $escape_wildcards && $db->string_quoting['escape_pattern'] && $db->string_quoting['escape'] !== $db->string_quoting['escape_pattern'] ) { $value.= $this->patternEscapeString(); } return $value; } // }}} // {{{ _quoteInteger() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteInteger($value, $quote, $escape_wildcards) { return (int)$value; } // }}} // {{{ _quoteText() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that already contains any DBMS specific * escaped character sequences. * @access protected */ function _quoteText($value, $quote, $escape_wildcards) { if (!$quote) { return $value; } $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $value = $db->escape($value, $escape_wildcards); if (MDB2::isError($value)) { return $value; } return "'".$value."'"; } // }}} // {{{ _readFile() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _readFile($value) { $close = false; if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) { $close = true; if (strtolower($match[1]) == 'file://') { $value = $match[2]; } $value = @fopen($value, 'r'); } if (is_resource($value)) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $fp = $value; $value = ''; while (!@feof($fp)) { $value.= @fread($fp, $db->options['lob_buffer_length']); } if ($close) { @fclose($fp); } } return $value; } // }}} // {{{ _quoteLOB() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteLOB($value, $quote, $escape_wildcards) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if ($db->options['lob_allow_url_include']) { $value = $this->_readFile($value); if (MDB2::isError($value)) { return $value; } } return $this->_quoteText($value, $quote, $escape_wildcards); } // }}} // {{{ _quoteCLOB() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteCLOB($value, $quote, $escape_wildcards) { return $this->_quoteLOB($value, $quote, $escape_wildcards); } // }}} // {{{ _quoteBLOB() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteBLOB($value, $quote, $escape_wildcards) { return $this->_quoteLOB($value, $quote, $escape_wildcards); } // }}} // {{{ _quoteBoolean() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteBoolean($value, $quote, $escape_wildcards) { return ($value ? 1 : 0); } // }}} // {{{ _quoteDate() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteDate($value, $quote, $escape_wildcards) { if ($value === 'CURRENT_DATE') { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (isset($db->function) && is_object($this->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) { return $db->function->now('date'); } return 'CURRENT_DATE'; } return $this->_quoteText($value, $quote, $escape_wildcards); } // }}} // {{{ _quoteTimestamp() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteTimestamp($value, $quote, $escape_wildcards) { if ($value === 'CURRENT_TIMESTAMP') { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (isset($db->function) && is_object($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) { return $db->function->now('timestamp'); } return 'CURRENT_TIMESTAMP'; } return $this->_quoteText($value, $quote, $escape_wildcards); } // }}} // {{{ _quoteTime() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteTime($value, $quote, $escape_wildcards) { if ($value === 'CURRENT_TIME') { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (isset($db->function) && is_object($this->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) { return $db->function->now('time'); } return 'CURRENT_TIME'; } return $this->_quoteText($value, $quote, $escape_wildcards); } // }}} // {{{ _quoteFloat() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteFloat($value, $quote, $escape_wildcards) { if (preg_match('/^(.*)e([-+])(\d+)$/i', $value, $matches)) { $decimal = $this->_quoteDecimal($matches[1], $quote, $escape_wildcards); $sign = $matches[2]; $exponent = str_pad($matches[3], 2, '0', STR_PAD_LEFT); $value = $decimal.'E'.$sign.$exponent; } else { $value = $this->_quoteDecimal($value, $quote, $escape_wildcards); } return $value; } // }}} // {{{ _quoteDecimal() /** * Convert a text value into a DBMS specific format that is suitable to * compose query statements. * * @param string $value text string value that is intended to be converted. * @param bool $quote determines if the value should be quoted and escaped * @param bool $escape_wildcards if to escape escape wildcards * @return string text string that represents the given argument value in * a DBMS specific format. * @access protected */ function _quoteDecimal($value, $quote, $escape_wildcards) { $value = (string)$value; $value = preg_replace('/[^\d\.,\-+eE]/', '', $value); if (preg_match('/[^\.\d]/', $value)) { if (strpos($value, ',')) { // 1000,00 if (!strpos($value, '.')) { // convert the last "," to a "." $value = strrev(str_replace(',', '.', strrev($value))); // 1.000,00 } elseif (strpos($value, '.') && strpos($value, '.') < strpos($value, ',')) { $value = str_replace('.', '', $value); // convert the last "," to a "." $value = strrev(str_replace(',', '.', strrev($value))); // 1,000.00 } else { $value = str_replace(',', '', $value); } } } return $value; } // }}} // {{{ writeLOBToFile() /** * retrieve LOB from the database * * @param resource $lob stream handle * @param string $file name of the file into which the LOb should be fetched * @return mixed MDB2_OK on success, a MDB2 error on failure * @access protected */ function writeLOBToFile($lob, $file) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match)) { if ($match[1] == 'file://') { $file = $match[2]; } } $fp = @fopen($file, 'wb'); while (!@feof($lob)) { $result = @fread($lob, $db->options['lob_buffer_length']); $read = strlen($result); if (@fwrite($fp, $result, $read) != $read) { @fclose($fp); return $db->raiseError(MDB2_ERROR, null, null, 'could not write to the output file', __FUNCTION__); } } @fclose($fp); return MDB2_OK; } // }}} // {{{ _retrieveLOB() /** * retrieve LOB from the database * * @param array $lob array * @return mixed MDB2_OK on success, a MDB2 error on failure * @access protected */ function _retrieveLOB(&$lob) { if (null === $lob['value']) { $lob['value'] = $lob['resource']; } $lob['loaded'] = true; return MDB2_OK; } // }}} // {{{ readLOB() /** * Read data from large object input stream. * * @param resource $lob stream handle * @param string $data reference to a variable that will hold data * to be read from the large object input stream * @param integer $length value that indicates the largest ammount ofdata * to be read from the large object input stream. * @return mixed the effective number of bytes read from the large object * input stream on sucess or an MDB2 error object. * @access public * @see endOfLOB() */ function _readLOB($lob, $length) { return substr($lob['value'], $lob['position'], $length); } // }}} // {{{ _endOfLOB() /** * Determine whether it was reached the end of the large object and * therefore there is no more data to be read for the its input stream. * * @param array $lob array * @return mixed true or false on success, a MDB2 error on failure * @access protected */ function _endOfLOB($lob) { return $lob['endOfLOB']; } // }}} // {{{ destroyLOB() /** * Free any resources allocated during the lifetime of the large object * handler object. * * @param resource $lob stream handle * @access public */ function destroyLOB($lob) { $lob_data = stream_get_meta_data($lob); $lob_index = $lob_data['wrapper_data']->lob_index; fclose($lob); if (isset($this->lobs[$lob_index])) { $this->_destroyLOB($this->lobs[$lob_index]); unset($this->lobs[$lob_index]); } return MDB2_OK; } // }}} // {{{ _destroyLOB() /** * Free any resources allocated during the lifetime of the large object * handler object. * * @param array $lob array * @access private */ function _destroyLOB(&$lob) { return MDB2_OK; } // }}} // {{{ implodeArray() /** * apply a type to all values of an array and return as a comma seperated string * useful for generating IN statements * * @access public * * @param array $array data array * @param string $type determines type of the field * * @return string comma seperated values */ function implodeArray($array, $type = false) { if (!is_array($array) || empty($array)) { return 'NULL'; } if ($type) { foreach ($array as $value) { $return[] = $this->quote($value, $type); } } else { $return = $array; } return implode(', ', $return); } // }}} // {{{ matchPattern() /** * build a pattern matching string * * @access public * * @param array $pattern even keys are strings, odd are patterns (% and _) * @param string $operator optional pattern operator (LIKE, ILIKE and maybe others in the future) * @param string $field optional field name that is being matched against * (might be required when emulating ILIKE) * * @return string SQL pattern */ function matchPattern($pattern, $operator = null, $field = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $match = ''; if (null !== $operator) { $operator = strtoupper($operator); switch ($operator) { // case insensitive case 'ILIKE': if (null === $field) { return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'case insensitive LIKE matching requires passing the field name', __FUNCTION__); } $db->loadModule('Function', null, true); $match = $db->function->lower($field).' LIKE '; break; case 'NOT ILIKE': if (null === $field) { return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'case insensitive NOT ILIKE matching requires passing the field name', __FUNCTION__); } $db->loadModule('Function', null, true); $match = $db->function->lower($field).' NOT LIKE '; break; // case sensitive case 'LIKE': $match = (null === $field) ? 'LIKE ' : ($field.' LIKE '); break; case 'NOT LIKE': $match = (null === $field) ? 'NOT LIKE ' : ($field.' NOT LIKE '); break; default: return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'not a supported operator type:'. $operator, __FUNCTION__); } } $match.= "'"; foreach ($pattern as $key => $value) { if ($key % 2) { $match.= $value; } else { $escaped = $db->escape($value); if (MDB2::isError($escaped)) { return $escaped; } $match.= $db->escapePattern($escaped); } } $match.= "'"; $match.= $this->patternEscapeString(); return $match; } // }}} // {{{ patternEscapeString() /** * build string to define pattern escape character * * @access public * * @return string define pattern escape character */ function patternEscapeString() { return ''; } // }}} // {{{ mapNativeDatatype() /** * Maps a native array description of a field to a MDB2 datatype and length * * @param array $field native field description * @return array containing the various possible types, length, sign, fixed * @access public */ function mapNativeDatatype($field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } // If the user has specified an option to map the native field // type to a custom MDB2 datatype... $db_type = strtok($field['type'], '(), '); if (!empty($db->options['nativetype_map_callback'][$db_type])) { return call_user_func_array($db->options['nativetype_map_callback'][$db_type], array($db, $field)); } // Otherwise perform the built-in (i.e. normal) MDB2 native type to // MDB2 datatype conversion return $this->_mapNativeDatatype($field); } // }}} // {{{ _mapNativeDatatype() /** * Maps a native array description of a field to a MDB2 datatype and length * * @param array $field native field description * @return array containing the various possible types, length, sign, fixed * @access public */ function _mapNativeDatatype($field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ mapPrepareDatatype() /** * Maps an mdb2 datatype to mysqli prepare type * * @param string $type * @return string * @access public */ function mapPrepareDatatype($type) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!empty($db->options['datatype_map'][$type])) { $type = $db->options['datatype_map'][$type]; if (!empty($db->options['datatype_map_callback'][$type])) { $parameter = array('type' => $type); return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter)); } } return $type; } } ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Function/000077500000000000000000000000001213666021100204335ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Function/Common.php000066400000000000000000000222411213666021100223750ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: Common.php 327310 2012-08-27 15:16:18Z danielc $ // /** * @package MDB2 * @category Database * @author Lukas Smith */ /** * Base class for the function modules that is extended by each MDB2 driver * * To load this module in the MDB2 object: * $mdb->loadModule('Function'); * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Driver_Function_Common extends MDB2_Module_Common { // {{{ executeStoredProc() /** * Execute a stored procedure and return any results * * @param string $name string that identifies the function to execute * @param mixed $params array that contains the paramaters to pass the stored proc * @param mixed $types array that contains the types of the columns in * the result set * @param mixed $result_class string which specifies which result class to use * @param mixed $result_wrap_class string which specifies which class to wrap results in * * @return mixed a result handle or MDB2_OK on success, a MDB2 error on failure * @access public */ function executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $error; } // }}} // {{{ functionTable() /** * return string for internal table used when calling only a function * * @return string for internal table used when calling only a function * @access public */ function functionTable() { return ''; } // }}} // {{{ now() /** * Return string to call a variable with the current timestamp inside an SQL statement * There are three special variables for current date and time: * - CURRENT_TIMESTAMP (date and time, TIMESTAMP type) * - CURRENT_DATE (date, DATE type) * - CURRENT_TIME (time, TIME type) * * @param string $type 'timestamp' | 'time' | 'date' * * @return string to call a variable with the current timestamp * @access public */ function now($type = 'timestamp') { switch ($type) { case 'time': return 'CURRENT_TIME'; case 'date': return 'CURRENT_DATE'; case 'timestamp': default: return 'CURRENT_TIMESTAMP'; } } // }}} // {{{ unixtimestamp() /** * return string to call a function to get the unix timestamp from a iso timestamp * * @param string $expression * * @return string to call a variable with the timestamp * @access public */ function unixtimestamp($expression) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $error; } // }}} // {{{ substring() /** * return string to call a function to get a substring inside an SQL statement * * @return string to call a function to get a substring * @access public */ function substring($value, $position = 1, $length = null) { if (null !== $length) { return "SUBSTRING($value FROM $position FOR $length)"; } return "SUBSTRING($value FROM $position)"; } // }}} // {{{ replace() /** * return string to call a function to get replace inside an SQL statement. * * @return string to call a function to get a replace * @access public */ function replace($str, $from_str, $to_str) { return "REPLACE($str, $from_str , $to_str)"; } // }}} // {{{ concat() /** * Returns string to concatenate two or more string parameters * * @param string $value1 * @param string $value2 * @param string $values... * * @return string to concatenate two strings * @access public */ function concat($value1, $value2) { $args = func_get_args(); return "(".implode(' || ', $args).")"; } // }}} // {{{ random() /** * return string to call a function to get random value inside an SQL statement * * @return return string to generate float between 0 and 1 * @access public */ function random() { return 'RAND()'; } // }}} // {{{ lower() /** * return string to call a function to lower the case of an expression * * @param string $expression * * @return return string to lower case of an expression * @access public */ function lower($expression) { return "LOWER($expression)"; } // }}} // {{{ upper() /** * return string to call a function to upper the case of an expression * * @param string $expression * * @return return string to upper case of an expression * @access public */ function upper($expression) { return "UPPER($expression)"; } // }}} // {{{ length() /** * return string to call a function to get the length of a string expression * * @param string $expression * * @return return string to get the string expression length * @access public */ function length($expression) { return "LENGTH($expression)"; } // }}} // {{{ guid() /** * Returns global unique identifier * * @return string to get global unique identifier * @access public */ function guid() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $error = $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); return $error; } // }}} } ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Manager/000077500000000000000000000000001213666021100202205ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Manager/Common.php000066400000000000000000001106651213666021100221720ustar00rootroot00000000000000 | // | Lorenzo Alberton | // +----------------------------------------------------------------------+ // // $Id: Common.php 327310 2012-08-27 15:16:18Z danielc $ // /** * @package MDB2 * @category Database * @author Lukas Smith * @author Lorenzo Alberton */ /** * Base class for the management modules that is extended by each MDB2 driver * * To load this module in the MDB2 object: * $mdb->loadModule('Manager'); * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Driver_Manager_Common extends MDB2_Module_Common { // {{{ splitTableSchema() /** * Split the "[owner|schema].table" notation into an array * * @param string $table [schema and] table name * * @return array array(schema, table) * @access private */ function splitTableSchema($table) { $ret = array(); if (strpos($table, '.') !== false) { return explode('.', $table); } return array(null, $table); } // }}} // {{{ getFieldDeclarationList() /** * Get declaration of a number of field in bulk * * @param array $fields a multidimensional associative array. * The first dimension determines the field name, while the second * dimension is keyed with the name of the properties * of the field being declared as array indexes. Currently, the types * of supported field properties are as follows: * * default * Boolean value to be used as default for this field. * * notnull * Boolean flag that indicates whether this field is constrained * to not be set to null. * * @return mixed string on success, a MDB2 error on failure * @access public */ function getFieldDeclarationList($fields) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!is_array($fields) || empty($fields)) { return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'missing any fields', __FUNCTION__); } foreach ($fields as $field_name => $field) { $query = $db->getDeclaration($field['type'], $field_name, $field); if (MDB2::isError($query)) { return $query; } $query_fields[] = $query; } return implode(', ', $query_fields); } // }}} // {{{ _fixSequenceName() /** * Removes any formatting in an sequence name using the 'seqname_format' option * * @param string $sqn string that containts name of a potential sequence * @param bool $check if only formatted sequences should be returned * @return string name of the sequence with possible formatting removed * @access protected */ function _fixSequenceName($sqn, $check = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $seq_pattern = '/^'.preg_replace('/%s/', '([a-z0-9_]+)', $db->options['seqname_format']).'$/i'; $seq_name = preg_replace($seq_pattern, '\\1', $sqn); if ($seq_name && !strcasecmp($sqn, $db->getSequenceName($seq_name))) { return $seq_name; } if ($check) { return false; } return $sqn; } // }}} // {{{ _fixIndexName() /** * Removes any formatting in an index name using the 'idxname_format' option * * @param string $idx string that containts name of anl index * @return string name of the index with eventual formatting removed * @access protected */ function _fixIndexName($idx) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $idx_pattern = '/^'.preg_replace('/%s/', '([a-z0-9_]+)', $db->options['idxname_format']).'$/i'; $idx_name = preg_replace($idx_pattern, '\\1', $idx); if ($idx_name && !strcasecmp($idx, $db->getIndexName($idx_name))) { return $idx_name; } return $idx; } // }}} // {{{ createDatabase() /** * create a new database * * @param string $name name of the database that should be created * @param array $options array with charset, collation info * * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function createDatabase($database, $options = array()) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ alterDatabase() /** * alter an existing database * * @param string $name name of the database that should be created * @param array $options array with charset, collation info * * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function alterDatabase($database, $options = array()) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ dropDatabase() /** * drop an existing database * * @param string $name name of the database that should be dropped * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function dropDatabase($database) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ _getCreateTableQuery() /** * Create a basic SQL query for a new table creation * * @param string $name Name of the database that should be created * @param array $fields Associative array that contains the definition of each field of the new table * @param array $options An associative array of table options * * @return mixed string (the SQL query) on success, a MDB2 error on failure * @see createTable() */ function _getCreateTableQuery($name, $fields, $options = array()) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!$name) { return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, 'no valid table name specified', __FUNCTION__); } if (empty($fields)) { return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, 'no fields specified for table "'.$name.'"', __FUNCTION__); } $query_fields = $this->getFieldDeclarationList($fields); if (MDB2::isError($query_fields)) { return $query_fields; } if (!empty($options['primary'])) { $query_fields.= ', PRIMARY KEY ('.implode(', ', array_keys($options['primary'])).')'; } $name = $db->quoteIdentifier($name, true); $result = 'CREATE '; if (!empty($options['temporary'])) { $result .= $this->_getTemporaryTableQuery(); } $result .= " TABLE $name ($query_fields)"; return $result; } // }}} // {{{ _getTemporaryTableQuery() /** * A method to return the required SQL string that fits between CREATE ... TABLE * to create the table as a temporary table. * * Should be overridden in driver classes to return the correct string for the * specific database type. * * The default is to return the string "TEMPORARY" - this will result in a * SQL error for any database that does not support temporary tables, or that * requires a different SQL command from "CREATE TEMPORARY TABLE". * * @return string The string required to be placed between "CREATE" and "TABLE" * to generate a temporary table, if possible. */ function _getTemporaryTableQuery() { return 'TEMPORARY'; } // }}} // {{{ createTable() /** * create a new table * * @param string $name Name of the database that should be created * @param array $fields Associative array that contains the definition of each field of the new table * The indexes of the array entries are the names of the fields of the table an * the array entry values are associative arrays like those that are meant to be * passed with the field definitions to get[Type]Declaration() functions. * array( * 'id' => array( * 'type' => 'integer', * 'unsigned' => 1 * 'notnull' => 1 * 'default' => 0 * ), * 'name' => array( * 'type' => 'text', * 'length' => 12 * ), * 'password' => array( * 'type' => 'text', * 'length' => 12 * ) * ); * @param array $options An associative array of table options: * array( * 'comment' => 'Foo', * 'temporary' => true|false, * ); * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function createTable($name, $fields, $options = array()) { $query = $this->_getCreateTableQuery($name, $fields, $options); if (MDB2::isError($query)) { return $query; } $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $result = $db->exec($query); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ dropTable() /** * drop an existing table * * @param string $name name of the table that should be dropped * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function dropTable($name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); $result = $db->exec("DROP TABLE $name"); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ truncateTable() /** * Truncate an existing table (if the TRUNCATE TABLE syntax is not supported, * it falls back to a DELETE FROM TABLE query) * * @param string $name name of the table that should be truncated * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function truncateTable($name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($name, true); $result = $db->exec("DELETE FROM $name"); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ vacuum() /** * Optimize (vacuum) all the tables in the db (or only the specified table) * and optionally run ANALYZE. * * @param string $table table name (all the tables if empty) * @param array $options an array with driver-specific options: * - timeout [int] (in seconds) [mssql-only] * - analyze [boolean] [pgsql and mysql] * - full [boolean] [pgsql-only] * - freeze [boolean] [pgsql-only] * * @return mixed MDB2_OK success, a MDB2 error on failure * @access public */ function vacuum($table = null, $options = array()) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ alterTable() /** * alter an existing table * * @param string $name name of the table that is intended to be changed. * @param array $changes associative array that contains the details of each type * of change that is intended to be performed. The types of * changes that are currently supported are defined as follows: * * name * * New name for the table. * * add * * Associative array with the names of fields to be added as * indexes of the array. The value of each entry of the array * should be set to another associative array with the properties * of the fields to be added. The properties of the fields should * be the same as defined by the MDB2 parser. * * * remove * * Associative array with the names of fields to be removed as indexes * of the array. Currently the values assigned to each entry are ignored. * An empty array should be used for future compatibility. * * rename * * Associative array with the names of fields to be renamed as indexes * of the array. The value of each entry of the array should be set to * another associative array with the entry named name with the new * field name and the entry named Declaration that is expected to contain * the portion of the field declaration already in DBMS specific SQL code * as it is used in the CREATE TABLE statement. * * change * * Associative array with the names of the fields to be changed as indexes * of the array. Keep in mind that if it is intended to change either the * name of a field and any other properties, the change array entries * should have the new names of the fields as array indexes. * * The value of each entry of the array should be set to another associative * array with the properties of the fields to that are meant to be changed as * array entries. These entries should be assigned to the new values of the * respective properties. The properties of the fields should be the same * as defined by the MDB2 parser. * * Example * array( * 'name' => 'userlist', * 'add' => array( * 'quota' => array( * 'type' => 'integer', * 'unsigned' => 1 * ) * ), * 'remove' => array( * 'file_limit' => array(), * 'time_limit' => array() * ), * 'change' => array( * 'name' => array( * 'length' => '20', * 'definition' => array( * 'type' => 'text', * 'length' => 20, * ), * ) * ), * 'rename' => array( * 'sex' => array( * 'name' => 'gender', * 'definition' => array( * 'type' => 'text', * 'length' => 1, * 'default' => 'M', * ), * ) * ) * ) * * @param boolean $check indicates whether the function should just check if the DBMS driver * can perform the requested table alterations if the value is true or * actually perform them otherwise. * @access public * * @return mixed MDB2_OK on success, a MDB2 error on failure */ function alterTable($name, $changes, $check) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ listDatabases() /** * list all databases * * @return mixed array of database names on success, a MDB2 error on failure * @access public */ function listDatabases() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implementedd', __FUNCTION__); } // }}} // {{{ listUsers() /** * list all users * * @return mixed array of user names on success, a MDB2 error on failure * @access public */ function listUsers() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ listViews() /** * list all views in the current database * * @param string database, the current is default * NB: not all the drivers can get the view names from * a database other than the current one * @return mixed array of view names on success, a MDB2 error on failure * @access public */ function listViews($database = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ listTableViews() /** * list the views in the database that reference a given table * * @param string table for which all referenced views should be found * @return mixed array of view names on success, a MDB2 error on failure * @access public */ function listTableViews($table) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ listTableTriggers() /** * list all triggers in the database that reference a given table * * @param string table for which all referenced triggers should be found * @return mixed array of trigger names on success, a MDB2 error on failure * @access public */ function listTableTriggers($table = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ listFunctions() /** * list all functions in the current database * * @return mixed array of function names on success, a MDB2 error on failure * @access public */ function listFunctions() { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ listTables() /** * list all tables in the current database * * @param string database, the current is default. * NB: not all the drivers can get the table names from * a database other than the current one * @return mixed array of table names on success, a MDB2 error on failure * @access public */ function listTables($database = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ listTableFields() /** * list all fields in a table in the current database * * @param string $table name of table that should be used in method * @return mixed array of field names on success, a MDB2 error on failure * @access public */ function listTableFields($table) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ createIndex() /** * Get the stucture of a field into an array * * @param string $table name of the table on which the index is to be created * @param string $name name of the index to be created * @param array $definition associative array that defines properties of the index to be created. * Currently, only one property named FIELDS is supported. This property * is also an associative with the names of the index fields as array * indexes. Each entry of this array is set to another type of associative * array that specifies properties of the index that are specific to * each field. * * Currently, only the sorting property is supported. It should be used * to define the sorting direction of the index. It may be set to either * ascending or descending. * * Not all DBMS support index sorting direction configuration. The DBMS * drivers of those that do not support it ignore this property. Use the * function supports() to determine whether the DBMS driver can manage indexes. * * Example * array( * 'fields' => array( * 'user_name' => array( * 'sorting' => 'ascending' * ), * 'last_login' => array() * ) * ) * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function createIndex($table, $name, $definition) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $table = $db->quoteIdentifier($table, true); $name = $db->quoteIdentifier($db->getIndexName($name), true); $query = "CREATE INDEX $name ON $table"; $fields = array(); foreach (array_keys($definition['fields']) as $field) { $fields[] = $db->quoteIdentifier($field, true); } $query .= ' ('. implode(', ', $fields) . ')'; $result = $db->exec($query); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ dropIndex() /** * drop existing index * * @param string $table name of table that should be used in method * @param string $name name of the index to be dropped * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function dropIndex($table, $name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $name = $db->quoteIdentifier($db->getIndexName($name), true); $result = $db->exec("DROP INDEX $name"); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ listTableIndexes() /** * list all indexes in a table * * @param string $table name of table that should be used in method * @return mixed array of index names on success, a MDB2 error on failure * @access public */ function listTableIndexes($table) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ _getAdvancedFKOptions() /** * Return the FOREIGN KEY query section dealing with non-standard options * as MATCH, INITIALLY DEFERRED, ON UPDATE, ... * * @param array $definition * @return string * @access protected */ function _getAdvancedFKOptions($definition) { return ''; } // }}} // {{{ createConstraint() /** * create a constraint on a table * * @param string $table name of the table on which the constraint is to be created * @param string $name name of the constraint to be created * @param array $definition associative array that defines properties of the constraint to be created. * The full structure of the array looks like this: *
     *          array (
     *              [primary] => 0
     *              [unique]  => 0
     *              [foreign] => 1
     *              [check]   => 0
     *              [fields] => array (
     *                  [field1name] => array() // one entry per each field covered
     *                  [field2name] => array() // by the index
     *                  [field3name] => array(
     *                      [sorting]  => ascending
     *                      [position] => 3
     *                  )
     *              )
     *              [references] => array(
     *                  [table] => name
     *                  [fields] => array(
     *                      [field1name] => array(  //one entry per each referenced field
     *                           [position] => 1
     *                      )
     *                  )
     *              )
     *              [deferrable] => 0
     *              [initiallydeferred] => 0
     *              [onupdate] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
     *              [ondelete] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
     *              [match] => SIMPLE|PARTIAL|FULL
     *          );
     *          
* @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function createConstraint($table, $name, $definition) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $table = $db->quoteIdentifier($table, true); $name = $db->quoteIdentifier($db->getIndexName($name), true); $query = "ALTER TABLE $table ADD CONSTRAINT $name"; if (!empty($definition['primary'])) { $query.= ' PRIMARY KEY'; } elseif (!empty($definition['unique'])) { $query.= ' UNIQUE'; } elseif (!empty($definition['foreign'])) { $query.= ' FOREIGN KEY'; } $fields = array(); foreach (array_keys($definition['fields']) as $field) { $fields[] = $db->quoteIdentifier($field, true); } $query .= ' ('. implode(', ', $fields) . ')'; if (!empty($definition['foreign'])) { $query.= ' REFERENCES ' . $db->quoteIdentifier($definition['references']['table'], true); $referenced_fields = array(); foreach (array_keys($definition['references']['fields']) as $field) { $referenced_fields[] = $db->quoteIdentifier($field, true); } $query .= ' ('. implode(', ', $referenced_fields) . ')'; $query .= $this->_getAdvancedFKOptions($definition); } $result = $db->exec($query); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ dropConstraint() /** * drop existing constraint * * @param string $table name of table that should be used in method * @param string $name name of the constraint to be dropped * @param string $primary hint if the constraint is primary * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function dropConstraint($table, $name, $primary = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $table = $db->quoteIdentifier($table, true); $name = $db->quoteIdentifier($db->getIndexName($name), true); $result = $db->exec("ALTER TABLE $table DROP CONSTRAINT $name"); if (MDB2::isError($result)) { return $result; } return MDB2_OK; } // }}} // {{{ listTableConstraints() /** * list all constraints in a table * * @param string $table name of table that should be used in method * @return mixed array of constraint names on success, a MDB2 error on failure * @access public */ function listTableConstraints($table) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ createSequence() /** * create sequence * * @param string $seq_name name of the sequence to be created * @param string $start start value of the sequence; default is 1 * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function createSequence($seq_name, $start = 1) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ dropSequence() /** * drop existing sequence * * @param string $seq_name name of the sequence to be dropped * @return mixed MDB2_OK on success, a MDB2 error on failure * @access public */ function dropSequence($name) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ listSequences() /** * list all sequences in the current database * * @param string database, the current is default * NB: not all the drivers can get the sequence names from * a database other than the current one * @return mixed array of sequence names on success, a MDB2 error on failure * @access public */ function listSequences($database = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} } ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Native/000077500000000000000000000000001213666021100200745ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Native/Common.php000066400000000000000000000067651213666021100220530ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: Common.php 242348 2007-09-09 13:47:36Z quipo $ // /** * Base class for the natuve modules that is extended by each MDB2 driver * * To load this module in the MDB2 object: * $mdb->loadModule('Native'); * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Driver_Native_Common extends MDB2_Module_Common { } ?>php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Reverse/000077500000000000000000000000001213666021100202615ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Driver/Reverse/Common.php000066400000000000000000000453541213666021100222350ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: Common.php 327310 2012-08-27 15:16:18Z danielc $ // /** * @package MDB2 * @category Database */ /** * These are constants for the tableInfo-function * they are bitwised or'ed. so if there are more constants to be defined * in the future, adjust MDB2_TABLEINFO_FULL accordingly */ define('MDB2_TABLEINFO_ORDER', 1); define('MDB2_TABLEINFO_ORDERTABLE', 2); define('MDB2_TABLEINFO_FULL', 3); /** * Base class for the schema reverse engineering module that is extended by each MDB2 driver * * To load this module in the MDB2 object: * $mdb->loadModule('Reverse'); * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Driver_Reverse_Common extends MDB2_Module_Common { // {{{ splitTableSchema() /** * Split the "[owner|schema].table" notation into an array * * @param string $table [schema and] table name * * @return array array(schema, table) * @access private */ function splitTableSchema($table) { $ret = array(); if (strpos($table, '.') !== false) { return explode('.', $table); } return array(null, $table); } // }}} // {{{ getTableFieldDefinition() /** * Get the structure of a field into an array * * @param string $table name of table that should be used in method * @param string $field name of field that should be used in method * @return mixed data array on success, a MDB2 error on failure. * The returned array contains an array for each field definition, * with all or some of these indices, depending on the field data type: * [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type] * @access public */ function getTableFieldDefinition($table, $field) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ getTableIndexDefinition() /** * Get the structure of an index into an array * * @param string $table name of table that should be used in method * @param string $index name of index that should be used in method * @return mixed data array on success, a MDB2 error on failure * The returned array has this structure: * * array ( * [fields] => array ( * [field1name] => array() // one entry per each field covered * [field2name] => array() // by the index * [field3name] => array( * [sorting] => ascending * ) * ) * ); * * @access public */ function getTableIndexDefinition($table, $index) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ getTableConstraintDefinition() /** * Get the structure of an constraints into an array * * @param string $table name of table that should be used in method * @param string $index name of index that should be used in method * @return mixed data array on success, a MDB2 error on failure * The returned array has this structure: *
     *          array (
     *              [primary] => 0
     *              [unique]  => 0
     *              [foreign] => 1
     *              [check]   => 0
     *              [fields] => array (
     *                  [field1name] => array() // one entry per each field covered
     *                  [field2name] => array() // by the index
     *                  [field3name] => array(
     *                      [sorting]  => ascending
     *                      [position] => 3
     *                  )
     *              )
     *              [references] => array(
     *                  [table] => name
     *                  [fields] => array(
     *                      [field1name] => array(  //one entry per each referenced field
     *                           [position] => 1
     *                      )
     *                  )
     *              )
     *              [deferrable] => 0
     *              [initiallydeferred] => 0
     *              [onupdate] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
     *              [ondelete] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
     *              [match] => SIMPLE|PARTIAL|FULL
     *          );
     *          
* @access public */ function getTableConstraintDefinition($table, $index) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ getSequenceDefinition() /** * Get the structure of a sequence into an array * * @param string $sequence name of sequence that should be used in method * @return mixed data array on success, a MDB2 error on failure * The returned array has this structure: *
     *          array (
     *              [start] => n
     *          );
     *          
* @access public */ function getSequenceDefinition($sequence) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $start = $db->currId($sequence); if (MDB2::isError($start)) { return $start; } if ($db->supports('current_id')) { $start++; } else { $db->warnings[] = 'database does not support getting current sequence value, the sequence value was incremented'; } $definition = array(); if ($start != 1) { $definition = array('start' => $start); } return $definition; } // }}} // {{{ getTriggerDefinition() /** * Get the structure of a trigger into an array * * EXPERIMENTAL * * WARNING: this function is experimental and may change the returned value * at any time until labelled as non-experimental * * @param string $trigger name of trigger that should be used in method * @return mixed data array on success, a MDB2 error on failure * The returned array has this structure: *
     *          array (
     *              [trigger_name]    => 'trigger name',
     *              [table_name]      => 'table name',
     *              [trigger_body]    => 'trigger body definition',
     *              [trigger_type]    => 'BEFORE' | 'AFTER',
     *              [trigger_event]   => 'INSERT' | 'UPDATE' | 'DELETE'
     *                  //or comma separated list of multiple events, when supported
     *              [trigger_enabled] => true|false
     *              [trigger_comment] => 'trigger comment',
     *          );
     *          
* The oci8 driver also returns a [when_clause] index. * @access public */ function getTriggerDefinition($trigger) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } // }}} // {{{ 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
         *   )
         *   
    * *
  • * * MDB2_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
         *   )
         *   
    * *
  • * * MDB2_TABLEINFO_ORDERTABLE * *

    Similar to MDB2_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 MDB2_PORTABILITY_FIX_CASE * turned on, the names of tables and fields will be lower or upper cased. * * @param object|string $result MDB2_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: * MDB2_TABLEINFO_ORDERTABLE, * MDB2_TABLEINFO_ORDER or * MDB2_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 MDB2_Error object on failure. * * @see MDB2_Driver_Common::setOption() */ function tableInfo($result, $mode = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if (!is_string($result)) { return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'method not implemented', __FUNCTION__); } $db->loadModule('Manager', null, true); $fields = $db->manager->listTableFields($result); if (MDB2::isError($fields)) { return $fields; } $flags = array(); $idxname_format = $db->getOption('idxname_format'); $db->setOption('idxname_format', '%s'); $indexes = $db->manager->listTableIndexes($result); if (MDB2::isError($indexes)) { $db->setOption('idxname_format', $idxname_format); return $indexes; } foreach ($indexes as $index) { $definition = $this->getTableIndexDefinition($result, $index); if (MDB2::isError($definition)) { $db->setOption('idxname_format', $idxname_format); return $definition; } if (count($definition['fields']) > 1) { foreach ($definition['fields'] as $field => $sort) { $flags[$field] = 'multiple_key'; } } } $constraints = $db->manager->listTableConstraints($result); if (MDB2::isError($constraints)) { return $constraints; } foreach ($constraints as $constraint) { $definition = $this->getTableConstraintDefinition($result, $constraint); if (MDB2::isError($definition)) { $db->setOption('idxname_format', $idxname_format); return $definition; } $flag = !empty($definition['primary']) ? 'primary_key' : (!empty($definition['unique']) ? 'unique_key' : false); if ($flag) { foreach ($definition['fields'] as $field => $sort) { if (empty($flags[$field]) || $flags[$field] != 'primary_key') { $flags[$field] = $flag; } } } } $res = array(); if ($mode) { $res['num_fields'] = count($fields); } foreach ($fields as $i => $field) { $definition = $this->getTableFieldDefinition($result, $field); if (MDB2::isError($definition)) { $db->setOption('idxname_format', $idxname_format); return $definition; } $res[$i] = $definition[0]; $res[$i]['name'] = $field; $res[$i]['table'] = $result; $res[$i]['type'] = preg_replace('/^([a-z]+).*$/i', '\\1', trim($definition[0]['nativetype'])); // 'primary_key', 'unique_key', 'multiple_key' $res[$i]['flags'] = empty($flags[$field]) ? '' : $flags[$field]; // not_null', 'unsigned', 'auto_increment', 'default_[rawencodedvalue]' if (!empty($res[$i]['notnull'])) { $res[$i]['flags'].= ' not_null'; } if (!empty($res[$i]['unsigned'])) { $res[$i]['flags'].= ' unsigned'; } if (!empty($res[$i]['auto_increment'])) { $res[$i]['flags'].= ' autoincrement'; } if (!empty($res[$i]['default'])) { $res[$i]['flags'].= ' default_'.rawurlencode($res[$i]['default']); } if ($mode & MDB2_TABLEINFO_ORDER) { $res['order'][$res[$i]['name']] = $i; } if ($mode & MDB2_TABLEINFO_ORDERTABLE) { $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; } } $db->setOption('idxname_format', $idxname_format); return $res; } } ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Extended.php000066400000000000000000000634271213666021100177000ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: Extended.php 327310 2012-08-27 15:16:18Z danielc $ /** * @package MDB2 * @category Database * @author Lukas Smith */ /** * Used by autoPrepare() */ define('MDB2_AUTOQUERY_INSERT', 1); define('MDB2_AUTOQUERY_UPDATE', 2); define('MDB2_AUTOQUERY_DELETE', 3); define('MDB2_AUTOQUERY_SELECT', 4); /** * MDB2_Extended: class which adds several high level methods to MDB2 * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Extended extends MDB2_Module_Common { // {{{ autoPrepare() /** * Generate an insert, update or delete query and call prepare() on it * * @param string table * @param array the fields names * @param int type of query to build * MDB2_AUTOQUERY_INSERT * MDB2_AUTOQUERY_UPDATE * MDB2_AUTOQUERY_DELETE * MDB2_AUTOQUERY_SELECT * @param string (in case of update and delete queries, this string will be put after the sql WHERE statement) * @param array that contains the types of the placeholders * @param mixed array that contains the types of the columns in * the result set or MDB2_PREPARE_RESULT, if set to * MDB2_PREPARE_MANIP the query is handled as a manipulation query * * @return resource handle for the query * @see buildManipSQL * @access public */ function autoPrepare($table, $table_fields, $mode = MDB2_AUTOQUERY_INSERT, $where = false, $types = null, $result_types = MDB2_PREPARE_MANIP) { $query = $this->buildManipSQL($table, $table_fields, $mode, $where); if (MDB2::isError($query)) { return $query; } $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $lobs = array(); foreach ((array)$types as $param => $type) { if (($type == 'clob') || ($type == 'blob')) { $lobs[$param] = $table_fields[$param]; } } return $db->prepare($query, $types, $result_types, $lobs); } // }}} // {{{ autoExecute() /** * Generate an insert, update or delete query and call prepare() and execute() on it * * @param string name of the table * @param array assoc ($key=>$value) where $key is a field name and $value its value * @param int type of query to build * MDB2_AUTOQUERY_INSERT * MDB2_AUTOQUERY_UPDATE * MDB2_AUTOQUERY_DELETE * MDB2_AUTOQUERY_SELECT * @param string (in case of update and delete queries, this string will be put after the sql WHERE statement) * @param array that contains the types of the placeholders * @param string which specifies which result class to use * @param mixed array that contains the types of the columns in * the result set or MDB2_PREPARE_RESULT, if set to * MDB2_PREPARE_MANIP the query is handled as a manipulation query * * @return bool|MDB2_Error true on success, a MDB2 error on failure * @see buildManipSQL * @see autoPrepare * @access public */ function autoExecute($table, $fields_values, $mode = MDB2_AUTOQUERY_INSERT, $where = false, $types = null, $result_class = true, $result_types = MDB2_PREPARE_MANIP) { $fields_values = (array)$fields_values; if ($mode == MDB2_AUTOQUERY_SELECT) { if (is_array($result_types)) { $keys = array_keys($result_types); } elseif (!empty($fields_values)) { $keys = $fields_values; } else { $keys = array(); } } else { $keys = array_keys($fields_values); } $params = array_values($fields_values); if (empty($params)) { $query = $this->buildManipSQL($table, $keys, $mode, $where); $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if ($mode == MDB2_AUTOQUERY_SELECT) { $result = $db->query($query, $result_types, $result_class); } else { $result = $db->exec($query); } } else { $stmt = $this->autoPrepare($table, $keys, $mode, $where, $types, $result_types); if (MDB2::isError($stmt)) { return $stmt; } $result = $stmt->execute($params, $result_class); $stmt->free(); } return $result; } // }}} // {{{ buildManipSQL() /** * Make automaticaly an sql query for prepare() * * Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), MDB2_AUTOQUERY_INSERT) * will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?) * NB : - This belongs more to a SQL Builder class, but this is a simple facility * - Be carefull ! If you don't give a $where param with an UPDATE/DELETE query, all * the records of the table will be updated/deleted ! * * @param string name of the table * @param ordered array containing the fields names * @param int type of query to build * MDB2_AUTOQUERY_INSERT * MDB2_AUTOQUERY_UPDATE * MDB2_AUTOQUERY_DELETE * MDB2_AUTOQUERY_SELECT * @param string (in case of update and delete queries, this string will be put after the sql WHERE statement) * * @return string sql query for prepare() * @access public */ function buildManipSQL($table, $table_fields, $mode, $where = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if ($db->options['quote_identifier']) { $table = $db->quoteIdentifier($table); } if (!empty($table_fields) && $db->options['quote_identifier']) { foreach ($table_fields as $key => $field) { $table_fields[$key] = $db->quoteIdentifier($field); } } if ((false !== $where) && (null !== $where)) { if (is_array($where)) { $where = implode(' AND ', $where); } $where = ' WHERE '.$where; } switch ($mode) { case MDB2_AUTOQUERY_INSERT: if (empty($table_fields)) { return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'Insert requires table fields', __FUNCTION__); } $cols = implode(', ', $table_fields); $values = '?'.str_repeat(', ?', (count($table_fields) - 1)); return 'INSERT INTO '.$table.' ('.$cols.') VALUES ('.$values.')'; break; case MDB2_AUTOQUERY_UPDATE: if (empty($table_fields)) { return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, 'Update requires table fields', __FUNCTION__); } $set = implode(' = ?, ', $table_fields).' = ?'; $sql = 'UPDATE '.$table.' SET '.$set.$where; return $sql; break; case MDB2_AUTOQUERY_DELETE: $sql = 'DELETE FROM '.$table.$where; return $sql; break; case MDB2_AUTOQUERY_SELECT: $cols = !empty($table_fields) ? implode(', ', $table_fields) : '*'; $sql = 'SELECT '.$cols.' FROM '.$table.$where; return $sql; break; } return $db->raiseError(MDB2_ERROR_SYNTAX, null, null, 'Non existant mode', __FUNCTION__); } // }}} // {{{ limitQuery() /** * Generates a limited query * * @param string query * @param array that contains the types of the columns in the result set * @param integer the numbers of rows to fetch * @param integer the row to start to fetching * @param string which specifies which result class to use * @param mixed string which specifies which class to wrap results in * * @return MDB2_Result|MDB2_Error result set on success, a MDB2 error on failure * @access public */ function limitQuery($query, $types, $limit, $offset = 0, $result_class = true, $result_wrap_class = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } $result = $db->setLimit($limit, $offset); if (MDB2::isError($result)) { return $result; } return $db->query($query, $types, $result_class, $result_wrap_class); } // }}} // {{{ execParam() /** * Execute a parameterized DML statement. * * @param string the SQL query * @param array if supplied, prepare/execute will be used * with this array as execute parameters * @param array that contains the types of the values defined in $params * * @return int|MDB2_Error affected rows on success, a MDB2 error on failure * @access public */ function execParam($query, $params = array(), $param_types = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } settype($params, 'array'); if (empty($params)) { return $db->exec($query); } $stmt = $db->prepare($query, $param_types, MDB2_PREPARE_MANIP); if (MDB2::isError($stmt)) { return $stmt; } $result = $stmt->execute($params); if (MDB2::isError($result)) { return $result; } $stmt->free(); return $result; } // }}} // {{{ getOne() /** * Fetch the first column of the first row of data returned from a query. * Takes care of doing the query and freeing the results when finished. * * @param string the SQL query * @param string that contains the type of the column in the result set * @param array if supplied, prepare/execute will be used * with this array as execute parameters * @param array that contains the types of the values defined in $params * @param int|string which column to return * * @return scalar|MDB2_Error data on success, a MDB2 error on failure * @access public */ function getOne($query, $type = null, $params = array(), $param_types = null, $colnum = 0) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } settype($params, 'array'); settype($type, 'array'); if (empty($params)) { return $db->queryOne($query, $type, $colnum); } $stmt = $db->prepare($query, $param_types, $type); if (MDB2::isError($stmt)) { return $stmt; } $result = $stmt->execute($params); if (!MDB2::isResultCommon($result)) { return $result; } $one = $result->fetchOne($colnum); $stmt->free(); $result->free(); return $one; } // }}} // {{{ getRow() /** * Fetch the first row of data returned from a query. Takes care * of doing the query and freeing the results when finished. * * @param string the SQL query * @param array that contains the types of the columns in the result set * @param array if supplied, prepare/execute will be used * with this array as execute parameters * @param array that contains the types of the values defined in $params * @param int the fetch mode to use * * @return array|MDB2_Error data on success, a MDB2 error on failure * @access public */ function getRow($query, $types = null, $params = array(), $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } settype($params, 'array'); if (empty($params)) { return $db->queryRow($query, $types, $fetchmode); } $stmt = $db->prepare($query, $param_types, $types); if (MDB2::isError($stmt)) { return $stmt; } $result = $stmt->execute($params); if (!MDB2::isResultCommon($result)) { return $result; } $row = $result->fetchRow($fetchmode); $stmt->free(); $result->free(); return $row; } // }}} // {{{ getCol() /** * Fetch a single column from a result set and return it as an * indexed array. * * @param string the SQL query * @param string that contains the type of the column in the result set * @param array if supplied, prepare/execute will be used * with this array as execute parameters * @param array that contains the types of the values defined in $params * @param int|string which column to return * * @return array|MDB2_Error data on success, a MDB2 error on failure * @access public */ function getCol($query, $type = null, $params = array(), $param_types = null, $colnum = 0) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } settype($params, 'array'); settype($type, 'array'); if (empty($params)) { return $db->queryCol($query, $type, $colnum); } $stmt = $db->prepare($query, $param_types, $type); if (MDB2::isError($stmt)) { return $stmt; } $result = $stmt->execute($params); if (!MDB2::isResultCommon($result)) { return $result; } $col = $result->fetchCol($colnum); $stmt->free(); $result->free(); return $col; } // }}} // {{{ getAll() /** * Fetch all the rows returned from a query. * * @param string the SQL query * @param array that contains the types of the columns in the result set * @param array if supplied, prepare/execute will be used * with this array as execute parameters * @param array that contains the types of the values defined in $params * @param int the fetch mode to use * @param bool if set to true, the $all will have the first * column as its first dimension * @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 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|MDB2_Error data on success, a MDB2 error on failure * @access public */ function getAll($query, $types = null, $params = array(), $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } settype($params, 'array'); if (empty($params)) { return $db->queryAll($query, $types, $fetchmode, $rekey, $force_array, $group); } $stmt = $db->prepare($query, $param_types, $types); if (MDB2::isError($stmt)) { return $stmt; } $result = $stmt->execute($params); if (!MDB2::isResultCommon($result)) { return $result; } $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group); $stmt->free(); $result->free(); return $all; } // }}} // {{{ getAssoc() /** * Fetch the entire result set of a query and return it as an * associative array using the first column as the key. * * If the result set contains more than two columns, the value * will be an array of the values from column 2-n. If the result * set contains only two columns, the returned value will be a * scalar with the value of the second column (unless forced to an * array with the $force_array parameter). A MDB2 error code is * returned on errors. If the result set contains fewer than two * columns, a MDB2_ERROR_TRUNCATED error is returned. * * For example, if the table 'mytable' contains: *
     *   ID      TEXT       DATE
     * --------------------------------
     *   1       'one'      944679408
     *   2       'two'      944679408
     *   3       'three'    944679408
     * 
* Then the call getAssoc('SELECT id,text FROM mytable') returns: *
     *    array(
     *      '1' => 'one',
     *      '2' => 'two',
     *      '3' => 'three',
     *    )
     * 
* ...while the call getAssoc('SELECT id,text,date FROM mytable') returns: *
     *    array(
     *      '1' => array('one', '944679408'),
     *      '2' => array('two', '944679408'),
     *      '3' => array('three', '944679408')
     *    )
     * 
* * If the more than one row occurs with the same value in the * first column, the last row overwrites all previous ones by * default. Use the $group parameter if you don't want to * overwrite like this. Example: *
     * getAssoc('SELECT category,id,name FROM mytable', null, null
     *           MDB2_FETCHMODE_ASSOC, false, true) returns:
     *    array(
     *      '1' => array(array('id' => '4', 'name' => 'number four'),
     *                   array('id' => '6', 'name' => 'number six')
     *             ),
     *      '9' => array(array('id' => '4', 'name' => 'number four'),
     *                   array('id' => '6', 'name' => 'number six')
     *             )
     *    )
     * 
* * Keep in mind that database functions in PHP usually return string * values for results regardless of the database's internal type. * * @param string the SQL query * @param array that contains the types of the columns in the result set * @param array if supplied, prepare/execute will be used * with this array as execute parameters * @param array that contains the types of the values defined in $params * @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 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|MDB2_Error data on success, a MDB2 error on failure * @access public */ function getAssoc($query, $types = null, $params = array(), $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $force_array = false, $group = false) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } settype($params, 'array'); if (empty($params)) { return $db->queryAll($query, $types, $fetchmode, true, $force_array, $group); } $stmt = $db->prepare($query, $param_types, $types); if (MDB2::isError($stmt)) { return $stmt; } $result = $stmt->execute($params); if (!MDB2::isResultCommon($result)) { return $result; } $all = $result->fetchAll($fetchmode, true, $force_array, $group); $stmt->free(); $result->free(); return $all; } // }}} // {{{ executeMultiple() /** * This function does several execute() calls on the same statement handle. * $params must be an array indexed numerically from 0, one execute call is * done for every 'row' in the array. * * If an error occurs during execute(), executeMultiple() does not execute * the unfinished rows, but rather returns that error. * * @param resource query handle from prepare() * @param array numeric array containing the data to insert into the query * * @return bool|MDB2_Error true on success, a MDB2 error on failure * @access public * @see prepare(), execute() */ function executeMultiple($stmt, $params = null) { if (MDB2::isError($stmt)) { return $stmt; } for ($i = 0, $j = count($params); $i < $j; $i++) { $result = $stmt->execute($params[$i]); if (MDB2::isError($result)) { return $result; } } return MDB2_OK; } // }}} // {{{ getBeforeID() /** * Returns the next free id of a sequence if the RDBMS * does not support auto increment * * @param string name of the table into which a new row was inserted * @param string name of the field into which a new row was inserted * @param bool when true the sequence is automatic created, if it not exists * @param bool if the returned value should be quoted * * @return int|MDB2_Error id on success, a MDB2 error on failure * @access public */ function getBeforeID($table, $field = null, $ondemand = true, $quote = true) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if ($db->supports('auto_increment') !== true) { $seq = $table.(empty($field) ? '' : '_'.$field); $id = $db->nextID($seq, $ondemand); if (!$quote || MDB2::isError($id)) { return $id; } return $db->quote($id, 'integer'); } elseif (!$quote) { return null; } return 'NULL'; } // }}} // {{{ getAfterID() /** * Returns the autoincrement ID if supported or $id * * @param mixed value as returned by getBeforeId() * @param string name of the table into which a new row was inserted * @param string name of the field into which a new row was inserted * * @return int|MDB2_Error id on success, a MDB2 error on failure * @access public */ function getAfterID($id, $table, $field = null) { $db = $this->getDBInstance(); if (MDB2::isError($db)) { return $db; } if ($db->supports('auto_increment') !== true) { return $id; } return $db->lastInsertID($table, $field); } // }}} } ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/Iterator.php000066400000000000000000000161601213666021100177210ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: Iterator.php 327310 2012-08-27 15:16:18Z danielc $ /** * PHP5 Iterator * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_Iterator implements Iterator { protected $fetchmode; /** * @var MDB2_Result_Common */ protected $result; protected $row; // {{{ constructor /** * Constructor */ public function __construct(MDB2_Result_Common $result, $fetchmode = MDB2_FETCHMODE_DEFAULT) { $this->result = $result; $this->fetchmode = $fetchmode; } // }}} // {{{ seek() /** * Seek forward to a specific row in a result set * * @param int number of the row where the data can be found * * @return void * @access public */ public function seek($rownum) { $this->row = null; if ($this->result) { $this->result->seek($rownum); } } // }}} // {{{ next() /** * Fetch next row of data * * @return void * @access public */ public function next() { $this->row = null; } // }}} // {{{ current() /** * return a row of data * * @return void * @access public */ public function current() { if (null === $this->row) { $row = $this->result->fetchRow($this->fetchmode); if (MDB2::isError($row)) { $row = false; } $this->row = $row; } return $this->row; } // }}} // {{{ valid() /** * Check if the end of the result set has been reached * * @return bool true/false, false is also returned on failure * @access public */ public function valid() { return (bool)$this->current(); } // }}} // {{{ free() /** * Free the internal resources associated with result. * * @return bool|MDB2_Error true on success, false|MDB2_Error if result is invalid * @access public */ public function free() { if ($this->result) { return $this->result->free(); } $this->result = false; $this->row = null; return false; } // }}} // {{{ key() /** * Returns the row number * * @return int|bool|MDB2_Error true on success, false|MDB2_Error if result is invalid * @access public */ public function key() { if ($this->result) { return $this->result->rowCount(); } return false; } // }}} // {{{ rewind() /** * Seek to the first row in a result set * * @return void * @access public */ public function rewind() { } // }}} // {{{ destructor /** * Destructor */ public function __destruct() { $this->free(); } // }}} } /** * PHP5 buffered Iterator * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_BufferedIterator extends MDB2_Iterator implements SeekableIterator { // {{{ valid() /** * Check if the end of the result set has been reached * * @return bool|MDB2_Error true on success, false|MDB2_Error if result is invalid * @access public */ public function valid() { if ($this->result) { return $this->result->valid(); } return false; } // }}} // {{{count() /** * Returns the number of rows in a result object * * @return int|MDB2_Error number of rows, false|MDB2_Error if result is invalid * @access public */ public function count() { if ($this->result) { return $this->result->numRows(); } return false; } // }}} // {{{ rewind() /** * Seek to the first row in a result set * * @return void * @access public */ public function rewind() { $this->seek(0); } // }}} } ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/MDB2/LOB.php000066400000000000000000000201671213666021100165460ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: LOB.php 222350 2006-10-25 11:52:21Z lsmith $ /** * @package MDB2 * @category Database * @author Lukas Smith */ require_once 'MDB2.php'; /** * MDB2_LOB: user land stream wrapper implementation for LOB support * * @package MDB2 * @category Database * @author Lukas Smith */ class MDB2_LOB { /** * contains the key to the global MDB2 instance array of the associated * MDB2 instance * * @var integer * @access protected */ var $db_index; /** * contains the key to the global MDB2_LOB instance array of the associated * MDB2_LOB instance * * @var integer * @access protected */ var $lob_index; // {{{ stream_open() /** * open stream * * @param string specifies the URL that was passed to fopen() * @param string the mode used to open the file * @param int holds additional flags set by the streams API * @param string not used * * @return bool * @access public */ function stream_open($path, $mode, $options, &$opened_path) { if (!preg_match('/^rb?\+?$/', $mode)) { return false; } $url = parse_url($path); if (empty($url['host'])) { return false; } $this->db_index = (int)$url['host']; if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) { return false; } $db =& $GLOBALS['_MDB2_databases'][$this->db_index]; $this->lob_index = (int)$url['user']; if (!isset($db->datatype->lobs[$this->lob_index])) { return false; } return true; } // }}} // {{{ stream_read() /** * read stream * * @param int number of bytes to read * * @return string * @access public */ function stream_read($count) { if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) { $db =& $GLOBALS['_MDB2_databases'][$this->db_index]; $db->datatype->_retrieveLOB($db->datatype->lobs[$this->lob_index]); $data = $db->datatype->_readLOB($db->datatype->lobs[$this->lob_index], $count); $length = strlen($data); if ($length == 0) { $db->datatype->lobs[$this->lob_index]['endOfLOB'] = true; } $db->datatype->lobs[$this->lob_index]['position'] += $length; return $data; } } // }}} // {{{ stream_write() /** * write stream, note implemented * * @param string data * * @return int * @access public */ function stream_write($data) { return 0; } // }}} // {{{ stream_tell() /** * return the current position * * @return int current position * @access public */ function stream_tell() { if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) { $db =& $GLOBALS['_MDB2_databases'][$this->db_index]; return $db->datatype->lobs[$this->lob_index]['position']; } } // }}} // {{{ stream_eof() /** * Check if stream reaches EOF * * @return bool * @access public */ function stream_eof() { if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) { return true; } $db =& $GLOBALS['_MDB2_databases'][$this->db_index]; $result = $db->datatype->_endOfLOB($db->datatype->lobs[$this->lob_index]); if (version_compare(phpversion(), "5.0", ">=") && version_compare(phpversion(), "5.1", "<") ) { return !$result; } return $result; } // }}} // {{{ stream_seek() /** * Seek stream, not implemented * * @param int offset * @param int whence * * @return bool * @access public */ function stream_seek($offset, $whence) { return false; } // }}} // {{{ stream_stat() /** * return information about stream * * @access public */ function stream_stat() { if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) { $db =& $GLOBALS['_MDB2_databases'][$this->db_index]; return array( 'db_index' => $this->db_index, 'lob_index' => $this->lob_index, ); } } // }}} // {{{ stream_close() /** * close stream * * @access public */ function stream_close() { if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) { $db =& $GLOBALS['_MDB2_databases'][$this->db_index]; if (isset($db->datatype->lobs[$this->lob_index])) { $db->datatype->_destroyLOB($db->datatype->lobs[$this->lob_index]); unset($db->datatype->lobs[$this->lob_index]); } } } // }}} } // register streams wrapper if (!stream_wrapper_register("MDB2LOB", "MDB2_LOB")) { MDB2::raiseError(); return false; } ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/000077500000000000000000000000001213666021100156575ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/CONTRIBUTORS000066400000000000000000000006451213666021100175440ustar00rootroot00000000000000PEAR DB Stig Baekken, Tomas V. Cox Metabase Manuel Lemos PEAR Error integration/XML Schema Parser Christian Dickmann Code Formatting Brent Cook MsSQL/Frontbase Driver Frank Kormann PgSQL Driver Paul Cooper Interbase/Firebird Lorenzo Alberton php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/MAINTAINERS000066400000000000000000000007271213666021100173620ustar00rootroot00000000000000Maintainers for MDB2 database backends/drivers: ibase : Lorenzo Alberton mssql : David Coallier mysqli : Lukas Smith oci8 : Lukas Smith pgsql : Lukas Smith fbsql : unmaintained sqlite : Lukas Smith querysim : Lukas Smith php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/README000066400000000000000000000173011213666021100165410ustar00rootroot00000000000000** Introduction: PEAR MDB2 is a project to merge PEAR DB and Metabase into one DB abstraction layer. You can get info on these at: PEAR DB: http://pear.php.net Metabase: http://phpclasses.upperdesign.com/browse.html/package/20/ At these URLs you will also find the licensing information on these two projects along with the credits. Actually MDB2 is the second major version of MDB. The main differences between the new MDB2 and the old MDB version is that the API has been drastically refactored to clean up the API and improve performance. If you have any questions or suggestions you can contact me (Lukas Smith) at this email address: smith@backendmedia.com Co-Author is Christopher Linn: clinn@backendmedia.com Or even better post a message to pear-dev@lists.php.net mailinglist for development related questions of the MDB2 package itself. For questions using MDB2 pelase direct your questions at pear-general@lists.php.net. ** Features MDB2 provides a common API for all support RDBMS. The main difference to most other DB abstraction packages is that MDB2 goes much further to ensure portability. Among other things MDB2 features: * An OO-style query API * A DSN (data source name) or array format for specifying database servers * Datatype abstraction and on demand datatype conversion * Portable error codes * Sequential and non sequential row fetching as well as bulk fetching * Ordered array and associative array for the fetched rows * Buffered and Unbuffered fetching * Prepare/execute (bind) emulation * Sequence emulation * Replace emulation * Limited Subselect emulation * Row limit support * Transactions support * Large Object support * Index/Unique support * Extension Framework to load advanced functionality on demand * Table information interface * RDBMS management methods (creating, dropping, altering) * RDBMS independent xml based schema definition management * Altering of a DB from a changed xml schema * Reverse engineering of xml schemas from an existing DB (currently MySQL and PgSQl) * Full integration into the PEAR Framework * PHPDoc API documentation ** Getting started: I would first recommend taking a look at example.php. This should give you a general feel of how to interact with MDB2. After that you may want to take a look at the rather large API at www.backendmedia.com/MDB2/docs. There you will also find a document describing the xml schema format and a little tutorial (it was just recently ported from Metabase, so it may contain errors). ** Current State: The current release can be found at the PEAR webpage: http://pear.php.net/package-info.php?package=MDB2 ** Package Content: As a user the only php script you will need to include is MDB2.php which will install to your PEAR root directory. All other files and their containing classes will be included via MDB2::factory(), MDB2::connect(), MDB2::singleton(). These will load additional classes. Most classes are loaded on demand. Furthermore MDB2 provides an extensive testing framework that works through a browser and command line interface. There are several other test that test the two wrappers. These files will install into your test dir found in the PEAR root dir. ** Documentation: You can find the still incomplete documentation for MDB2 here: http://pear.php.net/manual/en/package.database.mdb2.php PHPDoc generated documentation can be found at: http://www.backendmedia.com/MDB2/docs/ The entire "public" API and most of the "private" methods (except for some of the lob classes) have been documented with PHPDoc comments. Most of the API is borrowed from extPDO, so you can look there for detailed documentation. Since there are a large number of new methods available thanks to the Metabase heritage of MDB2 you will also have to take a look in the Metabase documentation (which can be found at the URL mentioned above, but does require that you register with phpclasses). Most of these Metabase functions have been renamed and changed considerably. The main things left are the datatypes and the manager module. For example ($db being an MDB2 object): $converted_value = MetabaseGetTimestampFieldValue($database, $value); would now be $converted_value = $db->quote($value, 'timestamp'); If you want to help out with documentation please email me. ** Testing: For most of the tests you can set the username/password/hostname in the relevant config file. The user will need to have the right to create new databases. test.php/clitest.php/testchoose.php: Is the native testing suite provided by MDB2. Please see the README in the tests directory for details. example.php: Several test calls to MDB2's native API. It require PEAR::VAR_Dump package and are configured to use the following settings: username = metapear password = funky hostname = localhost ** How to write new Drivers: Skeleton drivers are provided in the docs directory of the MDB2 package. The best course of action would be to take a MDB2 driver and hack it to fit the new RDBMS. This will surely be faster and it will ensure that the new driver takes full advantage of the MDB2 framework. I would however recommend working with the existing Metabase driver for inspiration that RDBMS when doing those changes. In order to check compliance of the driver with MDB2 you can use the testing suite (see the "testing" section above) ** History MDB was started after Manuel broad be into the discussion about getting the features of Metabase into PEAR that was going on (again) in December 2001. He suggested that I could take on this project. After alot of discussion about how when and if etc I started development in February 2002. MDB is based on Metabase but has been reworked severely to better match the PEAR DB API and PEAR CS. The approach I have taken so far is to take DB.php from PEAR DB and make it create a Metabase object. I have changed the Metabase structure slightly. The formatting has been reworked considerably to better fit the final structure (MDB standalone with a BC-wrapper for Metabase and PEAR DB), to fit the PEAR CS and to make it easier for other people to contribute. The metabase_interface.php was been renamed to metabase_wrapper.php and now only serves as a wrapper to keep BC with Metabase. A wrapper will have to be created for PEAR DB as well. Basically the result is a Metabase that is really close to the PEAR DB structure. I have also added any missing methods from PEAR DB. Work on moving the error handling to PEAR error handling is under way but still needs some work. In MDB2 the API was heavily refactored to be even more streamlined. Redundant features have been removed. Some features where moved out of the core into separate loadable modules. Instead of resources resultsets are now wrapped into objects similar to PEAR DB. ** Credits (never to early for those huh? :-) ): I would especially like to thank Manuel Lemos (Author of Metabase) for getting me involved in this and generally being around to ask questions. I would also like to thank Tomas Cox and Stig S. Bakken from the PEAR projects for help in undertstanding PEAR, solving problems and trusting me enough. Paul Cooper for the work on the pgsql driver. Furthermore I would like to thank Alex Black for being so enthusiastic about this project and offering binarycloud as a test bed for this project. Christian Dickmann for being the first to put MDB to some real use, making MDB use PEAR Error and working on the XML schema manager. Finally Peter Bowyer for starting the discussion that made people pick up this project again after the first versions of what was then called "metapear" have been ideling without much feedback. I guess I should also thank BackendMedia (my company :-) ) for providing the necessary means to develop this on company time (actually for the most part my entire life is company time ... so it goes)php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/STATUS000066400000000000000000000023551213666021100166320ustar00rootroot00000000000000STATUS OF THE PEAR MDB2 PACKAGE ============================= $Id: STATUS 212367 2006-05-03 16:07:41Z lsmith $ ------------------------------------------------------------------------ MDB2 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 pgsql mysqli sqlite FEATURE ibase | oci8 | mysql | mssql | querysim 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 pgsql mysqli sqlite TEST ibase | oci8 | mysql | mssql | querysim DBMS Versions Tested -------------------- fbsql ibase mssql mysql mysqli oci8 pgsql sqlite querysim Tests were performed under both of the following PHP versions unles otherwise noted: php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/TODO000066400000000000000000000000761213666021100163520ustar00rootroot00000000000000see http://oss.backendmedia.com/index.php?area=MDB2&page=ToDo php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/datatypes.html000066400000000000000000000174021213666021100205470ustar00rootroot00000000000000 MDB2 data types

MDB2 data types



Introduction

All DBMS provide multiple choice of data types for the information that can be stored in their database table fields. However, the set of data types made available varies from DBMS to DBMS.

To simplify the interface with the DBMS supported by MDB2 it was defined a base set of data types that applications may access independently of the underlying DBMS.

The MDB2 applications programming interface takes care of mapping data types when managing database options. It is also able to convert that is sent to and received from the underlying DBMS using the respective driver.

  • Text data type
  • The text data type is available with two options for the length: one that is explicitly length limited and another of undefined length that should be as large as the database allows.

    The length limited option is the most recommended for efficiency reasons. The undefined length option allows very large fields but may prevent the use of indexes and may not allow sorting on fields of its type.

    The fields of this type should be able to handle 8 bit characters. Drivers take care of DBMS specific escaping of characters of special meaning with the values of the strings to be converted to this type.

  • Boolean data type
  • The boolean data type represents only two values that can be either 1 or 0. Do not assume that these data types are stored as integers because some DBMS drivers may implement this type with single character text fields for a matter of efficient. Ternary logic is possible by using null as the third possible value that may be assigned to fields of this type.

  • Integer data type
  • The integer data type may store integer values as large as each DBMS may handle. Fields of this type may be created optionally as unsigned integers but not all DBMS support it. Therefore, such option may be ignored. Truly portable applications should not rely on the availability of this option.

  • Decimal data type
  • The decimal data type may store decimal numbers accurately with a fixed number of decimal places. This data type is suitable for representing accurate values like currency amounts.

    Some DBMS drivers may emulate the decimal data type using integers. Such drivers need to know in advance how many decimal places that should be used to perform eventual scale conversion when storing and retrieving values from a database. Despite this, applications may use arithmetic expressions and functions with the values stored on decimal type fields as long as any constant values that are used in the expressions are also converted with the respective MDB2 conversion functions.

    The number of places that are used to the left and the right of the decimal point is pre-determined and fixed for all decimal values stored in the same database. By default, MDB2 uses 2 places to the right of the decimal point, but this may be changed when setting the database connection. The number of places available to the right of the decimal point depend on the DBMS.

    It is not recommended to change the number places used to represent decimal values in database after it is installed. MDB2 does not keep track of changes in the number of decimal places.

  • Float data type
  • The float data type may store floating point decimal numbers. This data type is suitable for representing numbers within a large scale range that do not require high accuracy. The scale and the precision limits of the values that may be stored in a database depends on the DBMS that it is used.

  • Date data type
  • The date data type may represent dates with year, month and day. DBMS independent representation of dates is accomplished by using text strings formatted according to the IS0 8601 standard.

    The format defined by the ISO 8601 standard for dates is YYYY-MM-DD where YYYY is the number of the year (Gregorian calendar), MM is the number of the month from 1 to 12 and DD is the number of the day from 1 to 31. Months or days numbered below 10 should be padded on the left with 0.

    Some DBMS have native support for date formats, but for others the DBMS driver may have to represent them as integers or text values. In any case, it is always possible to make comparisons between date values as well sort query results by fields of this type.

  • Time data type
  • The time data type may represent the time of a given moment of the day. DBMS independent representation of the time of the day is also accomplished by using text strings formatted according to the IS0 8601 standard.

    The format defined by the ISO 8601 standard for the time of the day is HH:MI:SS where HH is the number of hour the day from 0 to 23 and MI and SS are respectively the number of the minute and of the second from 0 to 59. Hours, minutes and seconds numbered below 10 should be padded on the left with 0.

    Some DBMS have native support for time of the day formats, but for others the DBMS driver may have to represent them as integers or text values. In any case, it is always possible to make comparisons between time values as well sort query results by fields of this type.

  • Time stamp data type
  • The time stamp data type is a mere combination of the date and the time of the day data types. The representation of values of the time stamp type is accomplished by joining the date and time string values in a single string joined by a space. Therefore, the format template is YYYY-MM-DD HH:MI:SS. The represented values obey the same rules and ranges described for the date and time data types.

  • Large object (file) data types
  • The large object data types are meant to store data of undefined length that may be to large to store in text fields, like data that is usually stored in files.

    MDB2 supports two types of large object fields: Character Large OBjects (CLOBs) and Binary Large OBjects (BLOBs). CLOB fields are meant to store only data made of printable ASCII characters. BLOB fields are meant to store all types of data.

    Large object fields are usually not meant to be used as parameters of query search clause (WHERE) unless the underlying DBMS supports a feature usually known as "full text search".


    Manuel Lemos (mlemos@acm.org)
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/examples/000077500000000000000000000000001213666021100174755ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/examples/example.php000066400000000000000000000220201213666021100216350ustar00rootroot00000000000000PEAR-Error
    '; echo $error_obj->getMessage().': '.$error_obj->getUserinfo(); print ''; } PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'handle_pear_error'); // just for kicks you can mess up this part to see some pear error handling $user = 'root'; $pass = ''; $host = 'localhost'; $mdb2_name = 'metapear_test_db'; $mdb2_type = !empty($_GET['db_type']) ? $_GET['db_type'] : 'mysql'; echo($mdb2_type.'
    '); // Data Source Name: This is the universal connection string $dsn['username'] = $user; $dsn['password'] = $pass; $dsn['hostspec'] = $host; $dsn['phptype'] = $mdb2_type; // MDB2::factory will return a PEAR::MDB2 instance on success // or a Pear MDB2 error object on error // You can alternatively build a dsn here // $dsn = "$mdb2_type://$user:$pass@$host/$mdb2_name"; Var_Dump($dsn); $mdb2 =& MDB2::factory($dsn); // With MDB2::isError you can differentiate between an error or // a valid connection. if (MDB2::isError($mdb2)) { die (__LINE__.$mdb2->getMessage()); } // this loads the MDB2_Schema manager // this is a separate package you must install require_once 'MDB2/Schema.php'; // you can either pass a dsn string, a dsn array or an exisiting mdb2 connection $schema =& MDB2_Schema::factory($mdb2); $input_file = 'metapear_test_db.schema'; // lets create the database using 'metapear_test_db.schema' // if you have allready run this script you should have 'metapear_test_db.schema.before' // in that case MDB2 will just compare the two schemas and make any // necessary modifications to the existing database Var_Dump($schema->updateDatabase($input_file, $input_file.'.before')); echo('updating database from xml schema file
    '); echo('switching to database: '.$mdb2_name.'
    '); $mdb2->setDatabase($mdb2_name); // happy query $query ='SELECT * FROM test'; echo('query for the following examples:'.$query.'
    '); // run the query and get a result handler $result = $mdb2->query($query); // lets just get row:0 and free the result $array = $result->fetchRow(); $result->free(); echo('
    row:
    '); echo(Var_Dump($array).'
    '); $result = $mdb2->query($query); // lets just get row:0 and free the result $array = $result->fetchRow(MDB2_FETCHMODE_OBJECT); $result->free(); echo('
    row (object:
    '); echo(Var_Dump($array).'
    '); // run the query and get a result handler $result = $mdb2->query($query); // lets just get row:0 and free the result $array = $result->fetchRow(); $result->free(); echo('
    row from object:
    '); echo(Var_Dump($array).'
    '); // run the query and get a result handler $result = $mdb2->query($query); // lets just get column:0 and free the result $array = $result->fetchCol(2); $result->free(); echo('
    get column #2 (counting from 0):
    '); echo(Var_Dump($array).'
    '); // run the query and get a result handler $result = $mdb2->query($query); Var_Dump($mdb2->loadModule('Reverse', null, true)); echo('tableInfo:
    '); echo(Var_Dump($mdb2->reverse->tableInfo($result)).'
    '); $types = array('integer', 'text', 'timestamp'); $result->setResultTypes($types); $array = $result->fetchAll(MDB2_FETCHMODE_FLIPPED); $result->free(); echo('
    all with result set flipped:
    '); echo(Var_Dump($array).'
    '); // save some time with this function // lets just get all and free the result $array = $mdb2->queryAll($query); echo('
    all with just one call:
    '); echo(Var_Dump($array).'
    '); // run the query with the offset 1 and count 1 and get a result handler Var_Dump($mdb2->loadModule('Extended', null, false)); $result = $mdb2->extended->limitQuery($query, null, 1, 1); // lets just get everything but with an associative array and free the result $array = $result->fetchAll(MDB2_FETCHMODE_ASSOC); echo('
    associative array with offset 1 and count 1:
    '); echo(Var_Dump($array).'
    '); // lets create a sequence echo(Var_Dump($mdb2->loadModule('Manager', null, true))); echo('
    create a new seq with start 3 name real_funky_id
    '); $err = $mdb2->manager->createSequence('real_funky_id', 3); if (MDB2::isError($err)) { echo('
    could not create sequence again
    '); } echo('
    get the next id:
    '); $value = $mdb2->nextId('real_funky_id'); echo($value.'
    '); // lets try an prepare execute combo $alldata = array( array(1, 'one', 'un'), array(2, 'two', 'deux'), array(3, 'three', 'trois'), array(4, 'four', 'quatre') ); $stmt = $mdb2->prepare('INSERT INTO numbers VALUES(?,?,?)', array('integer', 'text', 'text'), MDB2_PREPARE_MANIP); foreach ($alldata as $row) { echo('running execute
    '); $stmt->bindValueArray($row); $stmt->execute(); } $array = array(4); echo('
    see getOne in action:
    '); echo(Var_Dump($mdb2->extended->getOne('SELECT trans_en FROM numbers WHERE number = ?',null,$array,array('integer'))).'
    '); $mdb2->setFetchmode(MDB2_FETCHMODE_ASSOC); echo('
    default fetchmode ist now MDB2_FETCHMODE_ASSOC
    '); echo('
    see getRow in action:
    '); echo(Var_Dump($mdb2->extended->getRow('SELECT * FROM numbers WHERE number = ?',array('integer','text','text'),$array, array('integer')))); echo('default fetchmode ist now MDB2_FETCHMODE_ORDERED
    '); $mdb2->setFetchmode(MDB2_FETCHMODE_ORDERED); echo('
    see getCol in action:
    '); echo(Var_Dump($mdb2->extended->getCol('SELECT * FROM numbers WHERE number != ?',null,$array,array('integer'), 1)).'
    '); echo('
    see getAll in action:
    '); echo(Var_Dump($mdb2->extended->getAll('SELECT * FROM test WHERE test_id != ?',array('integer','text','text'), $array, array('integer'))).'
    '); echo('
    see getAssoc in action:
    '); echo(Var_Dump($mdb2->extended->getAssoc('SELECT * FROM test WHERE test_id != ?',array('integer','text','text'), $array, array('integer'), MDB2_FETCHMODE_ASSOC)).'
    '); echo('tableInfo on a string:
    '); echo(Var_Dump($mdb2->reverse->tableInfo('numbers')).'
    '); echo('
    just a simple update query:
    '); echo('
    affected rows:
    '); echo(Var_Dump($mdb2->exec('UPDATE numbers set trans_en ='.$mdb2->quote(0, 'integer'))).'
    '); // subselect test $sub_select = $mdb2->subSelect('SELECT test_name from test WHERE test_name = '.$mdb2->quote('gummihuhn', 'text'), 'text'); echo(Var_Dump($sub_select).'
    '); $query_with_subselect = 'SELECT * FROM test WHERE test_name IN ('.$sub_select.')'; // run the query and get a result handler echo($query_with_subselect.'
    '); $result = $mdb2->query($query_with_subselect); $array = $result->fetchAll(); $result->free(); echo('
    all with subselect:
    '); echo('
    drop index (will fail if the index was never created):
    '); echo(Var_Dump($mdb2->manager->dropIndex('test', 'test_id_index')).'
    '); $index_def = array( 'fields' => array( 'test_id' => array( 'sorting' => 'ascending' ) ) ); echo('
    create index:
    '); echo(Var_Dump($mdb2->manager->createIndex('test', 'test_id_index', $index_def)).'
    '); if ($mdb2_type == 'mysql') { $schema->db->setOption('debug', true); $schema->db->setOption('log_line_break', '
    '); // ok now lets create a new xml schema file from the existing DB $database_definition = $schema->getDefinitionFromDatabase(); // we will not use the 'metapear_test_db.schema' for this // this feature is especially interesting for people that have an existing Db and want to move to MDB2's xml schema management // you can also try MDB2_MANAGER_DUMP_ALL and MDB2_MANAGER_DUMP_CONTENT echo(Var_Dump($schema->dumpDatabase( $database_definition, array( 'output_mode' => 'file', 'output' => $mdb2_name.'2.schema' ), MDB2_SCHEMA_DUMP_STRUCTURE )).'
    '); if ($schema->db->getOption('debug') === true) { echo($schema->db->getDebugOutput().'
    '); } // this is the database definition as an array echo(Var_Dump($database_definition).'
    '); } echo('
    just a simple delete query:
    '); echo(Var_Dump($mdb2->exec('DELETE FROM numbers')).'
    '); // You can disconnect from the database with: $mdb2->disconnect() ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/examples/example_php5.php000066400000000000000000000056431213666021100226050ustar00rootroot00000000000000
     'pgsql',
        'username' => 'postgres',
    #    'phptype'  => 'mysql',
    #    'username' => 'root',
        'password' => 'test',
        'hostspec' => 'localhost',
        'database' => 'driver_test',
    );
    #$dsn = 'sqlite:///:memory:';
    
    // create MDB2 instance
    $mdb2 = MDB2::factory($dsn);
    if (MDB2::isError($mdb2)) {
        die($mdb2->getMessage());
    }
    
    // set the default fetchmode
    $mdb2->setFetchMode(MDB2_FETCHMODE_ASSOC);
    
    $fields = array(
        'id' => array(
            'type'     => 'integer',
            'unsigned' => true,
            'autoincrement'  => true,
        ),
        'somename' => array(
            'type'     => 'text',
            'length'   => 12,
        ),
        'somedate'  => array(
            'type'     => 'date',
        ),
    );
    $table = 'sometable';
    
    // create a table
    // since we are on php5 we can use the magic __call() method to:
    // - load the manager module: $mdb2->loadModule('Manager', null, true);
    // - redirect the method call to the manager module: $mdb2->manager->createTable('sometable', $fields);
    $mdb2->mgCreateTable($table, $fields);
    
    $query = "INSERT INTO $table (somename, somedate) VALUES (:name, :date)";
    // parameters:
    // 1) the query (notice we are using named parameters, but we could also use ? instead
    // 2) types of the placeholders (either keyed numerically in order or by name)
    // 3) MDB2_PREPARE_MANIP denotes a DML statement
    $stmt = $mdb2->prepare($query, array('text', 'date'), MDB2_PREPARE_MANIP);
    if (MDB2::isError($stmt)) {
        die($stmt->getMessage());
    }
    
    // load Date helper class
    MDB2::loadFile('Date');
    
    $stmt->execute(array('name' => 'hello', 'date' => MDB2_Date::mdbToday()));
    // get the last inserted id
    echo 'last insert id: ';
    var_dump($mdb2->lastInsertId($table, 'id'));
    $stmt->execute(array('name' => 'world', 'date' => '2005-11-11'));
    // get the last inserted id
    echo 'last insert id: ';
    var_dump($mdb2->lastInsertId($table, 'id'));
    
    // load Iterator implementations
    MDB2::loadFile('Iterator');
    
    $query = 'SELECT * FROM '.$table;
    // parameters:
    // 1) the query
    // 2) true means MDB2 tries to determine the result set type automatically
    // 3) true is the default and means that internally a MDB2_Result instance should be created
    // 4) 'MDB2_BufferedIterator' means the MDB2_Result should be wrapped inside an SeekableIterator
    $result = $mdb2->query($query, true, true, 'MDB2_BufferedIterator');
    
    // iterate over the result set
    foreach ($result as $row) {
        echo 'output row:
    '; var_dump($row); } // call drop table, since dropTable is not implemented in our instance // but inside the loaded Manager module __call() will find it there and // will redirect the call accordingly // we could also have done: // $mdb2->manager->dropTable($table); or // $mdb2->mgDropTable($table); $mdb2->dropTable($table); ?> php-mdb2-2.5.0b5/MDB2-2.5.0b5/docs/examples/metapear_test_db.schema000066400000000000000000000031631213666021100241640ustar00rootroot00000000000000 metapear_test_db 1 numbers number integer 1 0 trans_en text 100 trans_fr text 100
    test test_id integer 1 0 test_name text 30 1 no name test_date timestamp 1 0000-00-00 00:00:00 test_id 1 test_name test0r test_date 2002-02-12 16:33:53 test_id 2 test_name gummihuhn test_date 2001-02-12 16:34:03
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/000077500000000000000000000000001213666021100160715ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/AllTests.php000066400000000000000000000026361213666021100203440ustar00rootroot00000000000000 */ // Keep tests from running twice when calling this file directly via PHPUnit. $call_main = false; if (strpos($_SERVER['argv'][0], 'phpunit') === false) { // Called via php, not PHPUnit. Pass the request to PHPUnit. if (!defined('PHPUnit_MAIN_METHOD')) { /** An indicator of which test was called. */ define('PHPUnit_MAIN_METHOD', 'AllTests::main'); $call_main = true; } } /** * Establish the test suite's environment. */ require_once __DIR__ . '/autoload.inc'; /** * This class is here to tell the Continuous Integration server that this * package should be tested * * See the README file for how to run the test suite manually. * * @package MDB2 * @category Database * @author Daniel Convissor */ class AllTests { public static function main() { PHPUnit_TextUI_TestRunner::run(self::suite()); } public static function suite() { $suite = new PHPUnit_Framework_TestSuite('MDB2 Unit Tests'); $dir = new GlobIterator(__DIR__ . '/Standard/*Test.php'); $suite->addTestFiles($dir); return $suite; } } if ($call_main) { AllTests::main(); } php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Debug.php000066400000000000000000000032321213666021100176300ustar00rootroot00000000000000_TEST_* environment variables here. * See the example in the code, below. * * @package MDB2 * @category Database * @author Daniel Convissor */ /* $_ENV['MYSQL_TEST_USER']='test'; $_ENV['MYSQL_TEST_PASSWD']='test'; $_ENV['MYSQL_TEST_DB']='test'; $_ENV['MYSQL_TEST_SOCKET']='/var/run/mysqld/mysqld.sock'; */ // Setting this here because this isn't the general test suite. error_reporting(E_ALL & ~E_STRICT & ~E_DEPRECATED); // Keep tests from running twice when calling this file directly via PHPUnit. $call_main = false; if (strpos($_SERVER['argv'][0], 'phpunit') === false) { // Called via php, not PHPUnit. Pass the request to PHPUnit. if (!defined('PHPUnit_MAIN_METHOD')) { /** An indicator of which test was called. */ define('PHPUnit_MAIN_METHOD', 'Debug::main'); $call_main = true; } } /** * Establish the test suite's environment. */ require_once __DIR__ . '/autoload.inc'; /** * A place to point debuggers for running specific tests * * To change the test called, edit addTestSuite()'s parameter. * * @package MDB2 * @category Database * @author Daniel Convissor */ class Debug { public static function main() { PHPUnit_TextUI_TestRunner::run(self::suite()); } public static function suite() { $suite = new PHPUnit_Framework_TestSuite('MDB2 Test Debugging'); $suite->addTestSuite('Standard_DatatypeTest'); return $suite; } } if ($call_main) { Debug::main(); } php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Nonstandard/000077500000000000000000000000001213666021100203445ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Nonstandard/Base.php000066400000000000000000000136651213666021100217420ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: Base.php 327312 2012-08-27 15:16:33Z danielc $ class Nonstandard_Base { /** * @var MDB2_Driver_Common */ public $db; /** * @var Standard_Abstract */ public $test; /** * Returns a driver-specific object * @param MDB2_Driver_Common * @param Standard_Abstract */ public static function factory($db, $test) { $classname = 'Nonstandard_' . ucfirst($db->phptype) . 'Helper'; if (class_exists($classname)) { $obj = new $classname; $obj->db = $db; $obj->test = $test; return $obj; } return false; } /** * Create a TRIGGER */ public function createTrigger($trigger_name, $table_name) { return $this->db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null, 'not capable', __FUNCTION__); } /** * Check if getTriggerDefinition() returns the correct definition for the trigger */ public function checkTrigger($trigger_name, $table_name, $def) { $this->test->assertEquals(strtoupper($trigger_name), strtoupper($def['trigger_name']), 'Error getting trigger definition (name)'); $this->test->assertEquals(strtoupper($table_name), strtoupper($def['table_name']), 'Error getting trigger definition (table)'); $this->test->assertEquals('AFTER', $def['trigger_type'], 'Error getting trigger definition (type)'); $this->test->assertEquals('UPDATE', $def['trigger_event'], 'Error getting trigger definition (event)'); $this->test->assertTrue(is_string($def['trigger_body']), 'Error getting trigger definition (body)'); $this->test->assertTrue($def['trigger_enabled'], 'Error getting trigger definition (enabled)'); //$this->test->assertTrue(empty($def['trigger_comment']), 'Error getting trigger definition (comment)'); } /** * Drop a TRIGGER */ public function dropTrigger($trigger_name, $table_name) { return $this->db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null, 'not capable', __FUNCTION__); } /** * Create a VIEW */ public function createView($view_name, $table_name) { $query = 'CREATE VIEW '. $this->db->quoteIdentifier($view_name, true) .' (id) AS SELECT id FROM ' . $this->db->quoteIdentifier($table_name, true) .' WHERE id > 1'; return $this->db->exec($query); } /** * Drop a VIEW */ public function dropView($view_name) { return $this->db->exec('DROP VIEW '.$view_name); } /** * Create a FUNCTION */ public function createFunction($name) { return $this->db->raiseError(MDB2_ERROR_NOT_CAPABLE, null, null, 'not capable', __FUNCTION__); } /** * Drop a FUNCTION */ public function dropFunction($name) { return $this->db->exec('DROP FUNCTION '.$name); } } php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Nonstandard/SqlservHelper.php000066400000000000000000000106371213666021100236630ustar00rootroot00000000000000 | // +----------------------------------------------------------------------+ // // $Id: MDB2_nonstandard_sqlsrv.php,v 1.2 2007/03/04 21:27:44 quipo Exp $ class Nonstandard_SqlsrvHelper extends Nonstandard_Base { public $trigger_body = ''; public function createTrigger($trigger_name, $table_name) { $this->trigger_body = 'CREATE TRIGGER '. $trigger_name .' ON '. $table_name .' FOR UPDATE AS DECLARE @oldName VARCHAR(100) DECLARE @newId INTEGER SELECT @oldName = (SELECT somename FROM Deleted) SELECT @newId = (SELECT id FROM Inserted) BEGIN UPDATE '. $table_name .' SET somedescription = @oldName WHERE id = @newId; END;'; return $this->db->exec($this->trigger_body); } public function checkTrigger($trigger_name, $table_name, $def) { parent::checkTrigger($trigger_name, $table_name, $def); $this->test->assertEquals($this->trigger_body, $def['trigger_body']); echo '
    ';
            var_dump($this->trigger_body);
            var_dump($def['trigger_body']);
        }
    
        public function dropTrigger($trigger_name, $table_name) {
            return $this->db->exec('DROP TRIGGER '.$trigger_name);
        }
        
        public function createFunction($name) {
            $query = 'CREATE FUNCTION '.$name.'(@Number1 Decimal(6,2), @Number2 Decimal(6,2))
    RETURNS Decimal(6,2)
    BEGIN
        DECLARE @Result Decimal(6,2)
        SET @Result = @Number1 + @Number2
        RETURN @Result
    END';
            return $this->db->exec($query);
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/README000066400000000000000000000013531213666021100167530ustar00rootroot00000000000000Configuration
    =============
    The test suite runs for every DBMS available for which you have supplied
    _TEST_* environment variables.  Read dsn.inc for complete information.
    
    
    Requirements
    ============
    The tests use PHPUnit 3.6.  See www.phpunit.de for more information.
    
    
    The Test Database
    =================
    The test suite automatically builds the needed objects.  But if it is
    necessary to create the objects manually, follow the instructions in
    dsn.inc, then run:
    
        php import.schema.php
    
    
    Execution
    =========
    To run the entire suite, call the following from the directory containing
    the tests directory:
    
        phpunit tests
    
    To run a particular set of tests, name the file to be run, for example:
    
        phpunit tests/Standard/ApiTest.php
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/000077500000000000000000000000001213666021100176315ustar00rootroot00000000000000php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/Abstract.php000066400000000000000000000300051213666021100221030ustar00rootroot00000000000000                                |
    // |          Daniel Convissor                           |
    // +----------------------------------------------------------------------+
    //
    // $Id: Abstract.php 322076 2012-01-11 15:51:09Z danielc $
    
    abstract class Standard_Abstract extends PHPUnit_Framework_TestCase {
        /**
         * Should the tables be cleared in the setUp() and tearDown() methods?
         * @var bool
         */
        protected $clear_tables = true;
    
        /**
         * The database name currently being tested
         * @var string
         */
        public $database;
    
        /**
         * The MDB2 object being currently tested
         * @var MDB2_Driver_Common
         */
        public $db;
    
        /**
         * The DSN of the database that is currently being tested
         * @var array
         */
        public $dsn;
    
        /**
         * The unserialized value of MDB2_TEST_SERIALIZED_DSNS
         * @var array
         */
        protected static $dsns;
    
        /**
         * Field names of the test table
         * @var array
         */
        public $fields = array(
                'user_name'     => 'text',
                'user_password' => 'text',
                'subscribed'    => 'boolean',
                'user_id'       => 'integer',
                'quota'         => 'decimal',
                'weight'        => 'float',
                'access_date'   => 'date',
                'access_time'   => 'time',
                'approved'      => 'timestamp',
        );
    
        /**
         * Options to use on the current database run
         * @var array
         */
        public $options;
    
        /**
         * @var string  the name of the users table
         */
        public $table_users = 'mdb2_users';
    
        /**
         * @var string  the name of the file table
         */
        public $table_files = 'mdb2_files';
    
    
        /**
         * Override PHPUnit's default behavior so authentication data doesn't
         * get broadcasted
         */
        protected function getDataSetAsString($strict = true) {
            return parent::getDataSetAsString(false);
        }
    
        public static function setUpBeforeClass() {
            $dsns = unserialize(MDB2_TEST_SERIALIZED_DSNS);
            self::$dsns = $dsns;
        }
    
        /**
         * A PHPUnit dataProvider callback to supply the connection info for tests
         * @uses mdb2_test_db_object_provider()
         * @return array  the $dsn and $options information for MDB2::factory()
         */
        public function provider() {
            return mdb2_test_db_object_provider();
        }
    
        /**
         * Establishes the class properties for each test
         *
         * Can not use setUp() because we are using a dataProvider to get multiple
         * MDB2 objects per test.
         *
         * @param array $ci  an associative array with two elements.  The "dsn"
         *                   element must contain an array of DSN information.
         *                   The "options" element must be an array of connection
         *                   options.
         */
        protected function manualSetUp($ci) {
            $this->db = MDB2::factory($ci['dsn'], $ci['options']);
            if (MDB2::isError($this->db)) {
                $this->markTestSkipped($this->db->getMessage());
            }
            $this->dsn = self::$dsns[$this->db->phptype]['dsn'];
            $this->options = self::$dsns[$this->db->phptype]['options'];
            $this->database = $this->db->getDatabase();
    
            $this->db->setDatabase($this->database);
            if ($this->database == ':memory:') {
                // Disable messages from other packages while building schema.
                $prior = error_reporting(E_ALL & ~E_STRICT & ~E_DEPRECATED);
                build_schema($this->db);
                error_reporting($prior);
            }
            $this->db->expectError(MDB2_ERROR_UNSUPPORTED);
            $this->clearTables();
        }
    
        public function tearDown() {
            if (!$this->db || MDB2::isError($this->db)) {
                return;
            }
            $this->clearTables();
            $this->db->disconnect();
            $this->db->popExpect();
            unset($this->db);
        }
    
    
        public function clearTables() {
            if (!$this->clear_tables) {
                return;
            }
            $this->db->exec('DELETE FROM ' . $this->table_users);
            $this->db->exec('DELETE FROM ' . $this->table_files);
        }
    
        public function supported($feature) {
            if (!$this->db->supports($feature)) {
                return false;
            }
            return true;
        }
    
        /**
         * Checks if a result is an MDB2 error and calls the
         * appropriate PHPUnit method if it is
         *
         * + MDB2_ERROR_UNSUPPORTED: markTestSkipped(not supported)
         * + MDB2_ERROR_NOT_CAPABLE: markTestSkipped(not supported)
         * + MDB2_ERROR_NO_PERMISSION: markTestSkipped(lacks permission)
         * + MDB2_ERROR_ACCESS_VIOLATION: markTestSkipped(lacks permission)
         * + Other errors: fail(error details)
         *
         * NOTE: calling PHPUnit's skip and fail methods causes the current
         * test to halt execution, so no conditional statements or other error
         * handling are needed by this method or the test methods calling this
         * method.
         *
         * @param mixed $result   the query result to inspect
         * @param string $action  a description of what is being checked
         * @return void
         */
        public function checkResultForErrors($result, $action)
        {
            if (MDB2::isError($result)) {
                if ($result->getCode() == MDB2_ERROR_UNSUPPORTED
                    || $result->getCode() == MDB2_ERROR_NOT_CAPABLE) {
                    $this->markTestSkipped("$action not supported");
                }
                if ($result->getCode() == MDB2_ERROR_NO_PERMISSION
                    || $result->getCode() == MDB2_ERROR_ACCESS_VIOLATION)
                {
                    $this->markTestSkipped("User lacks permission to $action");
                }
                $this->fail("$action ERROR: ".$result->getUserInfo());
            }
        }
    
        /**
         * @param MDB2_Result_Common $result  the query result to check
         * @param type $rownum  the row in the $result to check
         * @param type $data  the expected data
         * @return bool
         */
        public function verifyFetchedValues(&$result, $rownum, $data) {
            $row = $result->fetchRow(MDB2_FETCHMODE_ASSOC, $rownum);
            if (!is_array($row)) {
                $result->free();
                $this->fail('Error result row is not an array');
                return;
            }
    
            foreach ($this->fields as $field => $type) {
                $value = $row[$field];
                if ($type == 'float') {
                    $delta = 0.0000000001;
                } else {
                    $delta = 0;
                }
                $this->assertEquals($data[$field], $value, "the value retrieved for field \"$field\" doesn't match what was stored into the rownum $rownum", $delta);
            }
        }
    
        public function getSampleData($row = 1) {
            $data = array();
            $data['user_name']     = 'user_' . $row;
            $data['user_password'] = 'somepass';
            $data['subscribed']    = $row % 2 ? true : false;
            $data['user_id']       = $row;
            $data['quota']         = strval($row/100);
            $data['weight']        = sqrt($row);
            $data['access_date']   = MDB2_Date::mdbToday();
            $data['access_time']   = MDB2_Date::mdbTime();
            $data['approved']      = MDB2_Date::mdbNow();
            return $data;
        }
    
        /**
         * Populates the user table with some data and then returns the data for
         * later comparison
         *
         * @param int $rows  the number for rows to insert
         * @return array  a multi-dimensional associative array of the data inserted
         */
        public function populateUserData($rows = 1) {
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('populateUserData() problem loading module: ' . $result->getUserInfo());
            }
    
            $this->db->loadModule('Extended');
            $stmt = $this->db->extended->autoPrepare($this->table_users,
                array_keys($this->fields),
                MDB2_AUTOQUERY_INSERT, null, $this->fields);
    
            if (MDB2::isError($stmt)) {
                $this->fail('populateUserData() problem preparing statement: ' . $stmt->getUserInfo());
            }
    
            $data_save = array();
            $data_return = array();
            for ($i = 0; $i < $rows; $i++) {
                $row = $this->getSampleData($i);
                $data_save[] = array_values($row);
                $data_return[] = $row;
            }
    
            $result = $this->db->extended->executeMultiple($stmt, $data_save);
            if (MDB2::isError($result)) {
                $this->fail('populateUserData() problem inserting the data: ' . $result->getUserInfo());
            }
    
            return $data_return;
        }
    
        public function methodExists(&$class, $name) {
            if (is_object($class)
                && in_array(strtolower($name), array_map('strtolower', get_class_methods($class)))
            ) {
                return true;
            }
            //$this->fail('method '. $name.' not implemented in '.get_class($class));
            return false;
        }
    
        public function tableExists($table) {
            $this->db->loadModule('Manager', null, true);
            $tables = $this->db->manager->listTables();
            if (MDB2::isError($tables)) {
                //$this->fail('Cannot list tables: '. $tables->getUserInfo());
                return false;
            }
            return in_array(strtolower($table), array_map('strtolower', $tables));
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/ApiTest.php000066400000000000000000000344711213666021100217240ustar00rootroot00000000000000                                 |
    // +----------------------------------------------------------------------+
    //
    // $Id: ApiTest.php 327313 2012-08-27 15:16:42Z danielc $
    
    require_once dirname(__DIR__) . '/autoload.inc';
    
    class Standard_ApiTest extends Standard_Abstract {
        public $clear_tables = false;
    
        /**
         * @dataProvider provider
         */
        public function testParseDSN($ci) {
            $this->manualSetUp($ci);
    
            $expected = array (
                'phptype'  => 'phptype',
                'dbsyntax' => 'phptype',
                'username' => 'username',
                'password' => 'password',
                'protocol' => 'protocol',
                'hostspec' => false,
                'port'     => '110',
                'socket'   => false,
                'database' => '/usr/db_file.db',
                'mode'     => '0644',
            );
            $original = 'phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644';
            $this->assertEquals($expected, MDB2::parseDSN($original));
    
            // ---------------------------------------------------------------------
    
            $original = 'phptype(dbsyntax)://username:password@hostspec/database_name';
            $expected = array (
                'phptype'  => 'phptype',
                'dbsyntax' => 'dbsyntax',
                'username' => 'username',
                'password' => 'password',
                'protocol' => 'tcp',
                'hostspec' => 'hostspec',
                'port'     => false,
                'socket'   => false,
                'database' => 'database_name',
                'mode'     => false,
            );
            $this->assertEquals($expected, MDB2::parseDSN($original));
    
            // ---------------------------------------------------------------------
    
            $original = 'phptype://username:password@hostspec/database_name';
            $expected['dbsyntax'] = 'phptype';
            $this->assertEquals($expected, MDB2::parseDSN($original));
    
            // ---------------------------------------------------------------------
    
            $original = 'phptype://username:password@hostspec';
            $expected['database'] = false;
            $this->assertEquals($expected, MDB2::parseDSN($original));
    
            // ---------------------------------------------------------------------
    
            $original = 'phptype://username@hostspec';
            $expected['password'] = false;
            $this->assertEquals($expected, MDB2::parseDSN($original));
    
            // ---------------------------------------------------------------------
    
            $original = 'phptype://hostspec/database';
            $expected['username'] = false;
            $expected['database'] = 'database';
            $this->assertEquals($expected, MDB2::parseDSN($original));
    
            // ---------------------------------------------------------------------
    
            $original = 'phptype(dbsyntax)';
            $expected['database'] = false;
            $expected['hostspec'] = false;
            $expected['protocol'] = false;
            $expected['dbsyntax'] = 'dbsyntax';
            $this->assertEquals($expected, MDB2::parseDSN($original));
    
            // ---------------------------------------------------------------------
    
            //oracle's "Easy Connect" syntax (Oracle 10g, @see Bug #4854)
            $original = 'oci8://scott:tiger@//localhost/XE';
            $expected = array (
                'phptype'  => 'oci8',
                'dbsyntax' => 'oci8',
                'username' => 'scott',
                'password' => 'tiger',
                'protocol' => 'tcp',
                'hostspec' => '//localhost',
                'port'     => false,
                'socket'   => false,
                'database' => 'XE',
                'mode'     => false,
            );
            $this->assertEquals($expected, MDB2::parseDSN($original));
    
            // ---------------------------------------------------------------------
    
            //ibase dbname+path on windows
            $original = 'ibase://user:pwd@localhost/C:\\PATH_TO_DB\\TEST.FDB';
            $expected = array (
                'phptype'  => 'ibase',
                'dbsyntax' => 'ibase',
                'username' => 'user',
                'password' => 'pwd',
                'protocol' => 'tcp',
                'hostspec' => 'localhost',
                'port'     => false,
                'socket'   => false,
                'database' => 'C:\\PATH_TO_DB\\TEST.FDB',
                'mode'     => false,
            );
            $this->assertEquals($expected, MDB2::parseDSN($original));
    
            // ---------------------------------------------------------------------
    
            //sqlite dbname+path on unix
            $original = 'sqlite:////full/unix/path/to/file.db?mode=0666';
            $expected = array (
                'phptype'  => 'sqlite',
                'dbsyntax' => 'sqlite',
                'username' => false,
                'password' => false,
                'protocol' => 'tcp',
                'hostspec' => '',
                'port'     => false,
                'socket'   => false,
                'database' => '/full/unix/path/to/file.db',
                'mode'     => '0666',
            );
            $this->assertEquals($expected, MDB2::parseDSN($original));
        }
    
        //test stuff in common.php
        /**
         * @dataProvider provider
         */
        public function testConnect($ci) {
            $this->manualSetUp($ci);
    
            $db = MDB2::factory($this->dsn, $this->options);
            if (MDB2::isError($db)) {
                $this->fail('Connect failed bailing out - ' .$db->getMessage() . ' - ' .$db->getUserInfo());
            }
            if (MDB2::isError($this->db)) {
                exit;
            }
        }
    
        /**
         * @dataProvider provider
         */
        public function testGetOption($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db, 'getOption')) {
                return;
            }
            $option = $this->db->getOption('persistent');
            $this->assertEquals($option, $this->db->options['persistent']);
        }
    
        /**
         * @dataProvider provider
         */
        public function testSetOption($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db, 'setOption')) {
                return;
            }
            $option = $this->db->getOption('persistent');
            $this->db->setOption('persistent', !$option);
            $this->assertEquals(!$option, $this->db->getOption('persistent'));
            $this->db->setOption('persistent', $option);
        }
    
        /**
         * @dataProvider provider
         */
        public function testLoadModule($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db, 'loadModule')) {
                return;
            }
            $this->assertTrue(!MDB2::isError($this->db->loadModule('Manager', null, true)));
        }
    
        // test of the driver
        /**
         * A helper that executes a simple SELECT query
         * @return mixed  the query result on success, false on failure
         */
        public function standardQuery() {
            $query = 'SELECT * FROM ' . $this->table_users;
            if (!MDB2::isError($this->db)) {
                return $this->db->query($query);
            }
            return false;
        }
    
        /**
         * @dataProvider provider
         */
        public function testQuery($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db, 'query')) {
                return;
            }
            $result = $this->standardQuery();
            $this->assertTrue(MDB2::isResult($result), 'query: $result returned is not a resource');
            $this->assertTrue(MDB2::isResultCommon($result), 'query: $result returned is not a resource');
        }
    
        /**
         * @dataProvider provider
         */
        public function testExec($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db, 'exec')) {
                return;
            }
            $result = $this->db->exec('UPDATE ' . $this->table_users . ' SET user_name = user_name WHERE user_id = user_id');
            if (MDB2::isError($result)) {
                $this->fail('exec: $result returned is an error: '.$result->getMessage().' :: '.$result->getUserInfo());
            }
            $this->assertEquals(0, $result, 'exec: incorrect number of affected rows returned');
        }
    
        /**
         * @dataProvider provider
         */
        public function testPrepare($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db, 'prepare')) {
                return;
            }
            $stmt = $this->db->prepare('SELECT user_name FROM ' . $this->table_users . ' WHERE user_id = ?', array('integer'), MDB2_PREPARE_RESULT);
            $this->assertTrue(MDB2::isStatement($stmt));
            $stmt->free();
        }
    
        /**
         * @dataProvider provider
         */
        public function testFetchRow($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->standardQuery();
            if (!$this->methodExists($result, 'fetchRow')) {
                return;
            }
            $err = $result->fetchRow();
            $result->free();
    
            if (MDB2::isError($err)) {
                $this->fail('Error testFetch: '.$err->getMessage().' - '.$err->getUserInfo());
            }
        }
    
        /**
         * @dataProvider provider
         */
        public function testNumRows($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->standardQuery();
            if (!$this->methodExists($result, 'numRows')) {
                return;
            }
            $numrows = $result->numRows();
            $this->assertTrue(!MDB2::isError($numrows) && is_int($numrows));
            $result->free();
        }
    
        /**
         * @dataProvider provider
         */
        public function testNumCols($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->standardQuery();
            if (!$this->methodExists($result, 'numCols')) {
                return;
            }
            $numcols = $result->numCols();
            $this->assertTrue(!MDB2::isError($numcols) && $numcols > 0);
            $result->free();
        }
    
        /**
         * @dataProvider provider
         */
        public function testSingleton($ci) {
            $this->manualSetUp($ci);
    
            $db = MDB2::singleton();
            $this->assertTrue(MDB2::isConnection($db));
    
            // should have a different database name set
            $db = MDB2::singleton($this->dsn, $this->options);
    
            $this->assertTrue($db->db_index != $this->db->db_index);
        }
    
        /**
         * @dataProvider provider
         */
        public function testGetServerVersion($ci) {
            $this->manualSetUp($ci);
    
            $server_info = $this->db->getServerVersion(true);
            if (MDB2::isError($server_info)) {
                $this->fail('Error: '.$server_info->getMessage().' - '.$server_info->getUserInfo());
            } else {
                $this->assertTrue(is_string($server_info), 'Error: Server info is not returned as a string: '. serialize($server_info));
            }
            $server_info = $this->db->getServerVersion();
            if (MDB2::isError($server_info)) {
                $this->fail('Error: '.$server_info->getMessage().' - '.$server_info->getUserInfo());
            } else {
                $this->assertTrue(is_array($server_info), 'Error: Server info is not returned as an array: '. serialize($server_info));
            }
        }
    
        /**
         * @dataProvider provider
         */
        public function testQuoteIdentifier($ci) {
            $this->manualSetUp($ci);
    
            if ($this->db->phptype != 'ibase') {
                $start = $this->db->identifier_quoting['start'];
                $end = $this->db->identifier_quoting['end'];
    
                $string = 'test';
                $expected = $start . $string . $end;
                $this->assertEquals($expected, $this->db->quoteIdentifier($string, false), 'Error: identifier not quoted properly');
    
                $string = 'test.test';
                $expected = $start . 'test' . $end . '.' . $start . 'test' . $end;
                $this->assertEquals($expected, $this->db->quoteIdentifier($string, false), 'Error: identifier not quoted properly');
            }
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/BugsTest.php000066400000000000000000000643121213666021100221100ustar00rootroot00000000000000                                 |
    // +----------------------------------------------------------------------+
    //
    // $Id: BugsTest.php 328182 2012-10-29 15:10:30Z danielc $
    
    require_once dirname(__DIR__) . '/autoload.inc';
    
    class Standard_BugsTest extends Standard_Abstract {
        /**
         * @dataProvider provider
         */
        public function testFetchModeBug($ci) {
            $this->manualSetUp($ci);
    
            $data = array();
    
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', array_values($this->fields), MDB2_PREPARE_MANIP);
    
            $data['user_name'] = 'user_=';
            $data['user_password'] = 'somepass';
            $data['subscribed'] = true;
            $data['user_id'] = 0;
            $data['quota'] = sprintf("%.2f", strval(2/100));
            $data['weight'] = sqrt(0);
            $data['access_date'] = MDB2_Date::mdbToday();
            $data['access_time'] = MDB2_Date::mdbTime();
            $data['approved'] = MDB2_Date::mdbNow();
    
            $result = $stmt->execute(array_values($data));
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query '.$result->getMessage());
            }
    
            $stmt->free();
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users . ' ORDER BY user_name';
            $result = $this->db->query($query);
    
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: '.$result->getMessage());
            }
    
            $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
    
            $firstRow = $result->fetchRow();
            $this->assertEquals($firstRow['user_name'], $data['user_name'], 'The data returned does not match that expected');
    
            $result = $this->db->query('SELECT user_name, user_id, quota FROM ' . $this->table_users . ' ORDER BY user_name');
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: '.$result->getMessage());
            }
            $this->db->setFetchMode(MDB2_FETCHMODE_ORDERED);
    
            $value = $result->fetchOne();
            $this->assertEquals($data['user_name'], $value, 'The data returned does not match that expected');
            $result->free();
        }
    
        /**
         * @see http://bugs.php.net/bug.php?id=22328
         * @dataProvider provider
         */
        public function testBug22328($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->query('SELECT * FROM ' . $this->table_users);
            $this->db->pushErrorHandling(PEAR_ERROR_RETURN);
            $result2 = $this->db->query('SELECT * FROM foo');
    
            $data = $result->fetchRow();
            $this->db->popErrorHandling();
            $this->assertFalse(MDB2::isError($data), 'Error messages for a query affect result reading of other queries');
        }
    
        /**
         * @see http://pear.php.net/bugs/bug.php?id=670
         * @dataProvider provider
         */
        public function testBug670($ci) {
            $this->manualSetUp($ci);
    
            $data['user_name'] = null;
            $data['user_password'] = 'somepass';
            $data['subscribed'] = true;
            $data['user_id'] = 1;
            $data['quota'] = sprintf("%.2f",strval(3/100));
            $data['weight'] = sqrt(1);
            $data['access_date'] = MDB2_Date::mdbToday();
            $data['access_time'] = MDB2_Date::mdbTime();
            $data['approved'] = MDB2_Date::mdbNow();
    
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', array_values($this->fields), MDB2_PREPARE_MANIP);
            $result = $stmt->execute(array_values($data));
    
            $result = $this->db->query('SELECT user_name FROM ' . $this->table_users);
            $col = $result->fetchCol('user_name');
            if (MDB2::isError($col)) {
                $this->fail('Error when fetching column first first row as NULL: '.$col->getMessage());
            }
    
            $data['user_name'] = "user_1";
            $data['user_id'] = 2;
    
            $result = $stmt->execute(array_values($data));
    
            $result = $this->db->query('SELECT user_name FROM ' . $this->table_users);
            $col = $result->fetchCol('user_name');
            if (MDB2::isError($col)) {
                $this->fail('Error when fetching column: '.$col->getMessage());
            }
    
            $data['user_name'] = null;
    
            $stmt->free();
        }
    
        /**
         * @see http://pear.php.net/bugs/bug.php?id=681
         * @dataProvider provider
         */
        public function testBug681($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->query('SELECT * FROM ' . $this->table_users . ' WHERE 1=0');
    
            $numrows = $result->numRows();
            $this->assertEquals(0, $numrows, 'Numrows is not returning 0 for empty result sets');
    
            $data = $this->getSampleData(1);
    
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', array_values($this->fields), MDB2_PREPARE_MANIP);
            $result = $stmt->execute(array_values($data));
    
            $result = $this->db->query('SELECT * FROM ' . $this->table_users);
            $numrows = $result->numRows();
            $this->assertEquals(1, $numrows, 'Numrows is not returning proper value');
    
            $stmt->free();
        }
    
        /**
         * @see http://pear.php.net/bugs/bug.php?id=718
         * @dataProvider provider
         */
        public function testBug718($ci) {
            $this->manualSetUp($ci);
    
            $data = $this->getSampleData(1);
    
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', array_values($this->fields), MDB2_PREPARE_MANIP);
            $result = $stmt->execute(array_values($data));
    
            $row = $this->db->queryRow('SELECT a.user_id, b.user_id FROM ' . $this->table_users . ' a, ' . $this->table_users . ' b where a.user_id = b.user_id', array('integer', 'integer'), MDB2_FETCHMODE_ORDERED);
            $this->assertEquals(2, count($row), "Columns with the same name get overwritten in ordered mode");
    
            $stmt->free();
        }
    
        /**
         * @see http://pear.php.net/bugs/bug.php?id=946
         * @dataProvider provider
         */
        public function testBug946($ci) {
            $this->manualSetUp($ci);
    
            $data = array();
            $total_rows = 5;
    
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', array_values($this->fields), MDB2_PREPARE_MANIP);
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
    
                $result = $stmt->execute(array_values($data[$row]));
    
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: '.$result->getMessage());
                }
            }
            $stmt->free();
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
    
            $this->db->setLimit(3, 1);
            $result = $this->db->query($query);
            $numrows = $result->numRows();
            while ($row = $result->fetchRow()) {
                if (MDB2::isError($row)) {
                    $this->fail('Error fetching a row: '.$row->getMessage());
                }
            }
            $result->free();
    
            $result = $this->db->query($query);
            $numrows = $result->numRows();
            while ($row = $result->fetchRow()) {
                if (MDB2::isError($row)) {
                    $this->fail('Error fetching a row: '.$row->getMessage());
                }
            }
            $result->free();
        }
    
        /**
         * @see http://pear.php.net/bugs/bug.php?id=3146
         * @dataProvider provider
         */
        public function testBug3146($ci) {
            $this->manualSetUp($ci);
    
            $data = array();
            $total_rows = 5;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
    
                $result = $stmt->execute(array_values($data[$row]));
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: '.$result->getMessage());
                }
            }
            $stmt->free();
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users . ' ORDER BY user_id';
            $result = $this->db->query($query, $this->fields);
    
            $numrows = $result->numRows($result);
    
            $this->verifyFetchedValues($result, 0, $data[0]);
            $this->verifyFetchedValues($result, 2, $data[2]);
            $this->verifyFetchedValues($result, null, $data[3]);
            $this->verifyFetchedValues($result, 1, $data[1]);
    
            $result->free();
        }
    
        /**
         * Strong typing query result misbehaves when $n_columns > $n_types
         * @see http://pear.php.net/bugs/bug.php?id=9502
         * @dataProvider provider
         */
        public function testBug9502($ci) {
            $this->manualSetUp($ci);
    
            $row = 5;
            $data = $this->getSampleData($row);
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', array_values($this->fields), MDB2_PREPARE_MANIP);
            $result = $stmt->execute(array_values($data));
            $stmt->free();
    
            //provide an incomplete and scrambled types array
            $types = array();
            $types['subscribed'] = $this->fields['subscribed'];
            $types['user_name']  = $this->fields['user_name'];
            $types['weight']     = $this->fields['weight'];
    
            $query = 'SELECT weight, user_name, user_id, quota, subscribed FROM ' . $this->table_users . ' WHERE user_id = '.$row;
            $result = $this->db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($result)) {
                $this->fail('Error executing query: '.$result->getMessage() .' - '. $result->getUserInfo());
            } else {
                $this->assertInternalType('boolean', $result['subscribed']);
                $this->assertInternalType('numeric', $result['user_id']);
                $this->assertInternalType('float', $result['weight']);
                $this->assertInternalType('string', $result['user_name']);
            }
        }
    
        /**
         * Type introspection breaks with associative arrays if names are identical
         * @see http://pear.php.net/bugs/bug.php?id=18203
         * @dataProvider provider
         */
        public function testBug18203($ci) {
            $this->manualSetUp($ci);
    
            $res = $this->db->query("SELECT 1 as id, 2 as id, 'foo' as title", true);
            if (MDB2::isError($res)) {
                $this->fail($res->getMessage());
            }
            $record = $res->fetchRow(MDB2_FETCHMODE_ASSOC);
            $expected = array(
                'id'    => 2,
                'title' => 'foo'
            );
            $this->assertSame($expected, $record);
        }
    
        /**
         * Call to a member function seek() on a non-object
         * @see https://pear.php.net/bugs/bug.php?id=18978
         * @dataProvider provider
         */
        public function testBug18978($ci) {
            $this->manualSetUp($ci);
    
            $data = $this->populateUserData(3);
            $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
            MDB2::loadFile('Iterator');
    
            // This was test in bug.
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, true, true, 'MDB2_BufferedIterator');
            if (MDB2::isError($res)) {
                $this->fail($res->getUserInfo());
            }
            foreach($res as $key => $row) {
                $this->assertEquals($data[$key - 1]['user_name'], $row['user_name']);
            }
            $res->free();
    
            // Making sure direct instantiation works as well.
            $res = $this->db->query('SELECT * FROM ' . $this->table_users);
            $i = new MDB2_Iterator($res, MDB2_FETCHMODE_ASSOC);
            $i->seek(1);
            $row = $i->current();
            $this->assertEquals($data[1]['user_name'], $row['user_name']);
            unset($i);
            $res->free();
    
            // Make sure constructor type checking works.
            $this->setExpectedException('PHPUnit_Framework_Error', 'must be an instance of MDB2_Result_Common');
            $i = new MDB2_Iterator('foo');
        }
    
        /**
         * Make setOption('result_wrap_class') work without convoluted query() calls
         * @see https://pear.php.net/bugs/bug.php?id=16970
         * @dataProvider provider
         */
        public function testRequest16970($ci) {
            $this->manualSetUp($ci);
    
            $data = $this->populateUserData(1);
            $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
            MDB2::loadFile('Iterator');
    
            switch ($this->db->phptype) {
                case 'mysqli':
                    $expect = 'mysqli_result';
                    break;
                default:
                    $expect = 'resource';
            }
    
            // Regular behavior.
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users);
            $this->assertInstanceOf('MDB2_Result_Common', $res);
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, true);
            $this->assertInstanceOf('MDB2_Result_Common', $res);
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, false);
            if ($expect == 'resource') {
                $this->assertInternalType('resource', $res);
            } else {
                $this->assertInstanceOf($expect, $res);
            }
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, true, true);
            $this->assertInstanceOf('MDB2_Result_Common', $res);
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, true, false);
            $this->assertInstanceOf('MDB2_Result_Common', $res);
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, true, 'MDB2_BufferedIterator');
            $this->assertEquals('MDB2_BufferedIterator', get_class($res));
    
            // Setting third parameter to false forces raw results to be returned.
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, false, true);
            if ($expect == 'resource') {
                $this->assertInternalType('resource', $res);
            } else {
                $this->assertInstanceOf($expect, $res);
            }
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, false, false);
            if ($expect == 'resource') {
                $this->assertInternalType('resource', $res);
            } else {
                $this->assertInstanceOf($expect, $res);
            }
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, false, 'MDB2_BufferedIterator');
            if ($expect == 'resource') {
                $this->assertInternalType('resource', $res);
            } else {
                $this->assertInstanceOf($expect, $res);
            }
    
    
            // Utilize a default result wrap class.
    
            $this->db->setOption('result_wrap_class', 'MDB2_Iterator');
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users);
            $this->assertEquals('MDB2_Iterator', get_class($res));
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, true);
            $this->assertEquals('MDB2_Iterator', get_class($res));
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, false);
            if ($expect == 'resource') {
                $this->assertInternalType('resource', $res);
            } else {
                $this->assertInstanceOf($expect, $res);
            }
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, true, true);
            $this->assertEquals('MDB2_Iterator', get_class($res));
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, true, false);
            $this->assertInstanceOf('MDB2_Result_Common', $res);
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, true, 'MDB2_BufferedIterator');
            $this->assertEquals('MDB2_BufferedIterator', get_class($res));
    
            // Setting third parameter to false forces raw results to be returned.
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, false, true);
            if ($expect == 'resource') {
                $this->assertInternalType('resource', $res);
            } else {
                $this->assertInstanceOf($expect, $res);
            }
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, false, false);
            if ($expect == 'resource') {
                $this->assertInternalType('resource', $res);
            } else {
                $this->assertInstanceOf($expect, $res);
            }
    
            $res = $this->db->query('SELECT * FROM ' . $this->table_users, null, false, 'MDB2_BufferedIterator');
            if ($expect == 'resource') {
                $this->assertInternalType('resource', $res);
            } else {
                $this->assertInstanceOf($expect, $res);
            }
        }
    
        /**
         * non-static functions called statically
         * @see https://pear.php.net/bugs/bug.php?id=18398
         */
        public function testBug18398() {
            $oer = error_reporting(error_reporting() | E_STRICT);
            $dsn = array('phptype' => 'x');
            $db = new MDB2;
            $db->connect($dsn);
            error_reporting($oer);
        }
    
        /**
         * Multiple database handles seem to collide
         * @see http://pear.php.net/bugs/bug.php?id=15232
         * @dataProvider provider
         */
        public function testBug15232($ci) {
            $this->manualSetUp($ci);
    
            $data = $this->getSampleData(1);
    
            $this->db->beginTransaction();
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', array_values($this->fields), MDB2_PREPARE_MANIP);
            $result = $stmt->execute(array_values($data));
    
            $result = $this->db->query('SELECT * FROM ' . $this->table_users);
            $numrows = $result->numRows();
            $this->assertEquals(1, $numrows, 'First connection did not insert.');
    
            $ci['dsn']['new_link'] = true;
            $db2 = MDB2::factory($ci['dsn'], $ci['options']);
            if (MDB2::isError($db2)) {
                $this->markTestSkipped($db2->getMessage());
            }
            $database = $db2->getDatabase();
            $db2->setDatabase($database);
            $result = $db2->query('SELECT * FROM ' . $this->table_users);
            $numrows = $result->numRows();
            $this->assertEquals(0, $numrows, 'Second connection should get no results.');
    
            $stmt->free();
        }
    
        /**
         * compareDefinition() turns NULL defaults into empty strings for
         * NOT NULL columns
         * @see http://pear.php.net/bugs/bug.php?id=16280
         * @dataProvider provider
         */
        public function testBug16280($ci) {
            $this->manualSetUp($ci);
    
            $previous = array(
                'notnull' => true,
                'nativetype' => 'varchar',
                'length' => '50',
                'fixed' => false,
                'default' => '',
                'type' => 'text',
                'mdb2type' => 'text',
                'was' => 'name'
            );
            $current = array(
                'type' => 'text',
                'length' => '50',
                'notnull' => true,
                'was' => 'name'
            );
            $result = $this->db->compareDefinition($current, $previous);
            $this->assertEquals(array('default' => 1), $result);
        }
    
        /**
         * _compareIntegerDefinition() ignores length change
         * @see http://pear.php.net/bugs/bug.php?id=18494
         * @dataProvider provider
         */
        public function testBug18494($ci) {
            $this->manualSetUp($ci);
    
            $previous = array(
                'notnull' => true,
                'nativetype' => 'int',
                'length' => 4,
                'unsigned' => 1,
                'default' => 42,
                'type' => 'integer',
                'mdb2type' => 'integer',
                'was' => 'foo',
            );
            $current = array(
                'notnull' => true,
                'nativetype' => 'int',
                'length' => 8,
                'unsigned' => 1,
                'default' => 42,
                'type' => 'integer',
                'mdb2type' => 'integer',
                'was' => 'foo',
            );
            $result = $this->db->compareDefinition($current, $previous);
            $this->assertEquals(array('length' => 8), $result);
        }
    
        /**
         * Turning empty columns incorrectly to NULL
         * @see http://pear.php.net/bugs/bug.php?id=16314
         * @dataProvider provider
         */
        public function testBug16314($ci) {
            $this->manualSetUp($ci);
    
            $t = 'test_16314';
    
            $this->db->setOption('field_case', CASE_LOWER);
            $this->db->setOption('portability', MDB2_PORTABILITY_FIX_CASE | MDB2_PORTABILITY_ERRORS | MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES);
    
            $result = $this->db->exec("CREATE TABLE $t (id varchar(1) NOT NULL)");
            if (MDB2::isError($result)) {
                $this->fail('Error creating table: ' . $result->getMessage());
            }
    
            $stmt = $this->db->prepare("INSERT INTO $t VALUES (?)", null, MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $result = $this->db->exec("DROP TABLE $t");
                if (MDB2::isError($result)) {
                    $this->fail('Error dropping table: ' . $result->getMessage());
                }
                $this->fail('Prepare had problem: ' . $stmt->getMessage());
            }
    
            $result = $stmt->execute(array(''));
            if (MDB2::isError($result)) {
                $result = $this->db->exec("DROP TABLE $t");
                if (MDB2::isError($result)) {
                    $this->fail('Error dropping table: ' . $result->getMessage());
                }
                $this->fail('Error executing prepared query '.$result->getMessage());
            }
    
            $result = $this->db->exec("DROP TABLE $t");
            if (MDB2::isError($result)) {
                $this->fail('Error dropping table: ' . $result->getMessage());
            }
            $stmt->free();
        }
    
        /**
         * prepare(), execute() fail when statement combines placeholders and
         * null values
         * @see http://pear.php.net/bugs/bug.php?id=17270
         * @dataProvider provider
         */
        public function testBug17270($ci) {
            $this->manualSetUp($ci);
    
            $data = array(
                'name' => 'Abcd',
            );
            $types = array(
                'text',
            );
    
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (user_name, user_password) VALUES (:name, NULL)', $types, MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getMessage());
            }
    
            $result = $stmt->execute($data);
            if (MDB2::isError($result)) {
                $this->fail('Error executing query: ' . $result->getMessage());
            }
    
            $stmt->free();
        }
    
        /**
         * Memory Leak in MDB2_Error and/or PEAR_Error
         * @see http://pear.php.net/bugs/bug.php?id=12038
         */
        public function testBug12038() {
            $this->markTestSkipped("Bug still exists.");
    
            $mem_init = memory_get_usage();
            $mem_times = 2;
            $mem_stop = $mem_init * $mem_times;
    
            for ($row = 0; $row < 1000; $row++) {
                $pear = new PEAR;
                // Okay.
                //$pear->raiseError(null, 1, 'mode', array(), 'hi');
                //$pear->raiseError(null, 1, 'mode', array(), 'hi', 'StdClass', true);
                // Leaks
                $pear->raiseError(null, 1, 'mode', array(), 'hi', 'MDB2_Error', true);
                $mem_current = memory_get_usage();
                if ($mem_current > $mem_stop) {
                    $this->fail("Memory has gotten $mem_times times bigger.");
                }
            }
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/DatatypeTest.php000066400000000000000000001134751213666021100227700ustar00rootroot00000000000000            |
    // +----------------------------------------------------------------------+
    //
    // $Id: DatatypeTest.php 322076 2012-01-11 15:51:09Z danielc $
    
    require_once dirname(__DIR__) . '/autoload.inc';
    
    /**
     * A test callback function to be used in the test class below for
     * ensuring that custom datatype callback features are handled
     * correctly.
     *
     * @param MDB2   $db         The MDB2 database resource object.
     * @param string $method     The name of the MDB2_Driver_Datatype_Common method
     *                           the callback function was called from. One of
     *                           "getValidTypes", "convertResult", "getDeclaration",
     *                           "compareDefinition", "quote" and "mapPrepareDatatype".
     *                           See {@link MDB2_Driver_Datatype_Common} for the
     *                           details of what each method does.
     * @param array $aParameters An array of parameters, being the parameters that
     *                           were passed to the method calling the callback
     *                           function.
     * @return mixed Returns the appropriate value depending on the method that
     *               called the function. See {@link MDB2_Driver_Datatype_Common}
     *               for details of the expected return values of the five possible
     *               calling methods.
     */
    function datatype_test_callback(&$db, $method, $aParameters)
    {
        // Ensure the datatype module is loaded
        if (is_null($db->datatype)) {
            $db->loadModule('Datatype', null, true);
        }
        // Lowercase method names for PHP4/PHP5 compatibility
        $method = strtolower($method);
        switch($method) {
            // For all cases, return a string that identifies that the
            // callback method was able to call to the appropriate point
            case 'getvalidtypes':
                return 'datatype_test_callback::getvalidtypes';
            case 'convertresult':
                return 'datatype_test_callback::convertresult';
            case 'getdeclaration':
                return 'datatype_test_callback::getdeclaration';
            case 'comparedefinition':
                return 'datatype_test_callback::comparedefinition';
            case 'quote':
                return 'datatype_test_callback::quote';
            case 'mappreparedatatype':
                return 'datatype_test_callback::mappreparedatatype';
        }
    }
    
    /**
     * A test callback function to be used in the test class below for
     * ensuring that custom nativetype to datatype mapping is handled
     * correctly.
     *
     * @param MDB2 $db       The MDB2 database reource object.
     * @param array $aFields The standard array of fields produced from the
     *                       MySQL command "SHOW COLUMNS". See
     *                       {@link http://dev.mysql.com/doc/refman/5.0/en/describe.html}
     *                       for more details on the format of the fields.
     *                          "type"      The nativetype column type
     *                          "null"      "YES" or "NO"
     *                          "key"       "PRI", "UNI", "MUL", or null
     *                          "default"   The default value of the column
     *                          "extra"     "auto_increment", or null
     * @return array Returns an array of the following items:
     *                  0 => An array of possible MDB2 datatypes. As this is
     *                       a custom type, always has one entry, "test".
     *                  1 => The length of the type, if defined by the nativetype,
     *                       otherwise null.
     *                  2 => A boolean value indicating the "unsigned" nature of numeric
     *                       fields. Always null in this case, as this custom test
     *                       type is not numeric.
     *                  3 => A boolean value indicating the "fixed" nature of text
     *                       fields. Always bull in this case, as this custom test
     *                       type is not textual.
     */
    function nativetype_test_callback($db, $aFields)
    {
        // Prepare the type array
        $aType = array();
        $aType[] = 'test';
        // Can the length of the field be found?
        $length = null;
        $start = strpos($aFields['type'], '(');
        $end = strpos($aFields['type'], ')');
        if ($start && $end) {
            $start++;
            $chars = $end - $start;
            $length = substr($aFields['type'], $start, $chars);
        }
        // No unsigned value needed
        $unsigned = null;
        // No fixed value needed
        $fixed = null;
        return array($aType, $length, $unsigned, $fixed);
    }
    
    class Standard_DatatypeTest extends Standard_Abstract
    {
        // Test table name (it is dynamically created/dropped)
        public $table = 'datatypetable';
    
        /**
         * Can not use setUp() because we are using a dataProvider to get multiple
         * MDB2 objects per test.
         *
         * @param array $ci  an associative array with two elements.  The "dsn"
         *                   element must contain an array of DSN information.
         *                   The "options" element must be an array of connection
         *                   options.
         */
        protected function manualSetUp($ci) {
            parent::manualSetUp($ci);
    
            $this->db->loadModule('Manager', null, true);
            $this->fields = array(
                'id' => array(
                    'type'       => 'integer',
                    'unsigned'   => true,
                    'notnull'    => true,
                    'default'    => 0,
                ),
                'textfield'      => array(
                    'type'       => 'text',
                    'length'     => 12,
                ),
                'booleanfield'   => array(
                    'type'       => 'boolean',
                ),
                'decimalfield'   => array(
                    'type'       => 'decimal',
                ),
                'floatfield'     => array(
                    'type'       => 'float',
                ),
                'datefield'      => array(
                    'type'       => 'date',
                ),
                'timefield'      => array(
                    'type'       => 'time',
                ),
                'timestampfield' => array(
                    'type'       => 'timestamp',
                ),
            );
            if (!$this->tableExists($this->table)) {
                $this->db->manager->createTable($this->table, $this->fields);
            }
        }
    
        /**
         * The teardown method to clean up the testing environment.
         */
        public function tearDown() {
            if (!$this->db || MDB2::isError($this->db)) {
                return;
            }
            if ($this->tableExists($this->table)) {
                $this->db->manager->dropTable($this->table);
            }
            parent::tearDown();
        }
    
        /**
         * Get the types of each field given its name
         *
         * @param array $names list of field names
         * @return array $types list of matching field types
         * @dataProvider provider
         */
        public function getFieldTypes($names) {
            $types = array();
            foreach ($names as $name) {
                foreach ($this->fields as $fieldname => $field) {
                    if ($name == $fieldname) {
                        $types[$name] = $field['type'];
                    }
                }
            }
            return $types;
        }
    
        /**
         * Insert the values into the sample table
         *
         * @param array $values associative array (name => value)
         * @dataProvider provider
         */
        public function insertValues($values) {
            $types = $this->getFieldTypes(array_keys($values));
    
            $result = $this->db->exec('DELETE FROM '.$this->table);
            if (MDB2::isError($result)) {
                $this->assertTrue(false, 'Error emptying table: '.$result->getMessage());
            }
    
            $query = sprintf('INSERT INTO %s (%s) VALUES (%s)',
                $this->table,
                implode(', ', array_keys($values)),
                implode(', ', array_fill(0, count($values), '?'))
            );
            $stmt = $this->db->prepare($query, array_values($types), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->assertTrue(false, 'Error creating prepared query: '.$stmt->getMessage());
            }
            $result = $stmt->execute(array_values($values));
            if (MDB2::isError($result)) {
                $this->assertTrue(false, 'Error executing prepared query: '.$result->getMessage());
            }
            $stmt->free();
        }
    
        /**
         * Select the inserted row from the db and check the inserted values
         * @param array $values associative array (name => value) of inserted data
         * @dataProvider provider
         */
        public function selectAndCheck($values) {
            $types = $this->getFieldTypes(array_keys($values));
    
            $query = 'SELECT '. implode (', ', array_keys($values)). ' FROM '.$this->table;
            $result = $this->db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
            foreach ($values as $name => $value) {
                $this->assertEquals($result[$name], $values[$name], 'Error in '.$types[$name].' value: incorrect conversion');
            }
        }
    
        /**
         * Test the TEXT datatype for incorrect conversions
         * @dataProvider provider
         */
        public function testTextDataType($ci, $emulate_prepared = false) {
            $this->manualSetUp($ci);
    
            if ($emulate_prepared) {
                $this->db->setOption('emulate_prepared', true);
            }
    
            $data = array(
                'id'        => 1,
                'textfield' => 'test',
            );
            $this->insertValues($data);
            $this->selectAndCheck($data);
    
            if (!$emulate_prepared && !$this->db->getOption('emulate_prepared')) {
                $this->testTextDataType($ci, true);
            } elseif($emulate_prepared) {
                $this->db->setOption('emulate_prepared', false);
            }
        }
    
        /**
         * Test the DECIMAL datatype for incorrect conversions
         * @dataProvider provider
         */
        public function testDecimalDataType($ci, $emulate_prepared = false) {
            $this->manualSetUp($ci);
    
            if ($emulate_prepared) {
                $this->db->setOption('emulate_prepared', true);
            }
    
            $data = array(
                'id'           => 1,
                'decimalfield' => 10.35,
            );
            $this->insertValues($data);
            $this->selectAndCheck($data);
    
            $old_locale = setlocale(LC_NUMERIC, 0);
            if (OS_UNIX) {
                setlocale(LC_NUMERIC, 'de_DE@euro', 'de_DE', 'de', 'ge');
            } else {
                setlocale(LC_NUMERIC, 'de_DE@euro', 'de_DE', 'deu_deu');
            }
    
            $this->insertValues($data);
            $this->selectAndCheck($data);
    
            setlocale(LC_NUMERIC, $old_locale);
    
            $expected = 10.35;
    
            $actual = $this->db->quote($expected, 'decimal');
            $this->assertEquals($expected, $actual);
    
            $non_us = number_format($expected, 2, ',', '');
            $actual = $this->db->quote($non_us, 'decimal');
            $this->assertEquals($expected, $actual);
    
            $expected = 1000.35;
    
            $non_us = '1,000.35';
            $actual = $this->db->quote($non_us, 'decimal');
            $this->assertEquals($expected, $actual);
    
            $non_us = '1000,35';
            $actual = $this->db->quote($non_us, 'decimal');
            $this->assertEquals($expected, $actual);
    
            $non_us = '1.000,35';
            $actual = $this->db->quote($non_us, 'decimal');
            $this->assertEquals($expected, $actual);
    
            // test quoting with invalid chars
            $val = '100.3abc";d@a[\\';
            $this->assertEquals(100.3, $this->db->quote($val, 'decimal'));
    
            if (!$emulate_prepared && !$this->db->getOption('emulate_prepared')) {
                $this->testDecimalDataType($ci, true);
            } elseif($emulate_prepared) {
                $this->db->setOption('emulate_prepared', false);
            }
        }
    
        /**
         * Test the FLOAT datatype for incorrect conversions
         * @dataProvider provider
         */
        public function testFloatDataType($ci, $emulate_prepared = false) {
            $this->manualSetUp($ci);
    
            if ($emulate_prepared) {
                $this->db->setOption('emulate_prepared', true);
            }
    
            $data = array(
                'id'         => 1,
                'floatfield' => 10.35,
            );
            $this->insertValues($data);
            $this->selectAndCheck($data);
    
            $old_locale = setlocale(LC_NUMERIC, 0);
            if (OS_UNIX) {
                setlocale(LC_NUMERIC, 'de_DE@euro', 'de_DE', 'de', 'ge');
            } else {
                setlocale(LC_NUMERIC, 'de_DE@euro', 'de_DE', 'deu_deu');
            }
    
    
            $this->insertValues($data);
            $this->selectAndCheck($data);
    
            setlocale(LC_NUMERIC, $old_locale);
    
            $data['floatfield'] = '1.035e+1';
            $this->insertValues($data);
            $this->selectAndCheck($data);
    
            $data['floatfield'] = '1.035E+01';
            $this->insertValues($data);
            $this->selectAndCheck($data);
    
            $expected = '1.035E+01';
            $non_us = '1,035e+1';
            $actual = $this->db->quote($non_us, 'float');
            $this->assertEquals($expected, $actual);
    
            $expected = 10.35;
    
            $actual = $this->db->quote($expected, 'float');
            $this->assertEquals($expected, $actual);
    
            $non_us = number_format($expected, 2, ',', '');
            $actual = $this->db->quote($non_us, 'float');
            $this->assertEquals($expected, $actual);
    
            $expected = 1000.35;
    
            $non_us = '1,000.35';
            $actual = $this->db->quote($non_us, 'float');
            $this->assertEquals($expected, $actual);
    
            $non_us = '1000,35';
            $actual = $this->db->quote($non_us, 'float');
            $this->assertEquals($expected, $actual);
    
            $non_us = '1.000,35';
            $actual = $this->db->quote($non_us, 'float');
            $this->assertEquals($expected, $actual);
    
            // test quoting with invalid chars
            $val = '100.3abc";d@a[\\';
            $this->assertEquals(100.3, $this->db->quote($val, 'float'));
    
            if (!$emulate_prepared && !$this->db->getOption('emulate_prepared')) {
                $this->testFloatDataType($ci, true);
            } elseif($emulate_prepared) {
                $this->db->setOption('emulate_prepared', false);
            }
        }
    
        /**
         * Test the BOOLEAN datatype for incorrect conversions
         * @dataProvider provider
         */
        public function testBooleanDataType($ci, $emulate_prepared = false) {
            $this->manualSetUp($ci);
    
            if ($emulate_prepared) {
                $this->db->setOption('emulate_prepared', true);
            }
    
            $data = array(
                'id'          => 1,
                'booleanfield' => true,
            );
            $this->insertValues($data);
            $this->selectAndCheck($data);
    
            $data['booleanfield'] = false;
            $this->insertValues($data);
            $this->selectAndCheck($data);
    
            if (!$emulate_prepared && !$this->db->getOption('emulate_prepared')) {
                $this->testBooleanDataType($ci, true);
            } elseif($emulate_prepared) {
                $this->db->setOption('emulate_prepared', false);
            }
        }
    
        /**
         * Test the DATE datatype for incorrect conversions
         * @dataProvider provider
         */
        public function testDateDataType($ci, $emulate_prepared = false) {
            $this->manualSetUp($ci);
    
            if ($emulate_prepared) {
                $this->db->setOption('emulate_prepared', true);
            }
    
            $data = array(
                'id'        => 1,
                'datefield' => date('Y-m-d'),
            );
            $this->insertValues($data, 'date');
            $this->selectAndCheck($data);
    
            if (!$emulate_prepared && !$this->db->getOption('emulate_prepared')) {
                $this->testDateDataType($ci, true);
            } elseif($emulate_prepared) {
                $this->db->setOption('emulate_prepared', false);
            }
        }
    
        /**
         * Test the TIME datatype for incorrect conversions
         * @dataProvider provider
         */
        public function testTimeDataType($ci, $emulate_prepared = false) {
            $this->manualSetUp($ci);
    
            if ($emulate_prepared) {
                $this->db->setOption('emulate_prepared', true);
            }
    
            $data = array(
                'id'        => 1,
                'timefield' => date('H:i:s'),
            );
            $this->insertValues($data, 'time');
            $this->selectAndCheck($data);
    
            if (!$emulate_prepared && !$this->db->getOption('emulate_prepared')) {
                $this->testTimeDataType($ci, true);
            } elseif($emulate_prepared) {
                $this->db->setOption('emulate_prepared', false);
            }
        }
    
        /**
         * Test the TIMESTAMP datatype for incorrect conversions
         * @dataProvider provider
         */
        public function testTimestampDataType($ci, $emulate_prepared = false) {
            $this->manualSetUp($ci);
    
            if ($emulate_prepared) {
                $this->db->setOption('emulate_prepared', true);
            }
    
            $data = array(
                'id'            => 1,
                'timestampfield' => date('Y-m-d H:i:s'),
            );
            $this->insertValues($data, 'timestamp');
            $this->selectAndCheck($data);
    
            if (!$emulate_prepared && !$this->db->getOption('emulate_prepared')) {
                $this->testTimestampDataType($ci, true);
            } elseif($emulate_prepared) {
                $this->db->setOption('emulate_prepared', false);
            }
        }
    
        /**
         * Tests escaping of text values with special characters
         * @dataProvider provider
         */
        public function testEscapeSequences($ci) {
            $this->manualSetUp($ci);
    
            $test_strings = array(
                "'",
                "\"",
                "\\",
                "%",
                "_",
                "''",
                "\"\"",
                "\\\\",
                "\\'\\'",
                "\\\"\\\""
            );
    
            $this->clearTables();
            foreach($test_strings as $key => $string) {
                $value = $this->db->quote($string, 'text');
                $query = "INSERT INTO $this->table_users (user_name,user_id) VALUES ($value, $key)";
                $result = $this->db->exec($query);
    
                if (MDB2::isError($result)) {
                    $this->assertTrue(false, 'Error executing insert query'.$result->getMessage());
                }
    
                $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE user_id = '.$key;
                $value = $this->db->queryOne($query, 'text');
    
                if (MDB2::isError($value)) {
                    $this->assertTrue(false, 'Error executing select query'.$value->getMessage());
                }
    
                $this->assertEquals($string, $value, "the value retrieved for field \"user_name\" doesn't match what was stored");
            }
        }
    
        /**
         * Tests escaping of text pattern strings with special characters
         * @dataProvider provider
         */
        public function testPatternSequences($ci) {
            $this->manualSetUp($ci);
    
            switch ($this->db->phptype) {
                case 'sqlite':
                    // LIKE and GLOB are not case sensitive for ASCII.
                    // http://www.sqlite.org/lang_expr.html#like
                    $case_sensitive_expect = 3;
                    break;
                default:
                    $case_sensitive_expect = 2;
            }
    
            $test_strings = array(
                "Foo",
                "FOO",
                "foo",
            );
    
            $this->clearTables();
            foreach($test_strings as $key => $string) {
                $value = $this->db->quote($string, 'text');
                $query = "INSERT INTO $this->table_users (user_name,user_id) VALUES ($value, $key)";
                $result = $this->db->exec($query);
                if (MDB2::isError($result)) {
                    $this->assertTrue(false, 'Error executing insert query'.$result->getMessage());
                }
            }
    
            $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE '
                . $this->db->datatype->matchPattern(array('F', '%'), 'LIKE', 'user_name');
            $values = $this->db->queryCol($query, 'text');
            $this->assertEquals($case_sensitive_expect, count($values), "case sensitive search was expected to return 2 rows but returned: ".count($values));
    
            // NOTE: if this test fails on mysql, it's due to table/field having
            // binary collation.
            $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE '
                . $this->db->datatype->matchPattern(array('foo'), 'ILIKE', 'user_name');
            $values = $this->db->queryCol($query, 'text');
            $this->assertEquals(3, count($values), "case insensitive search was expected to return 3 rows but returned: ".count($values));
    
            $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE '
                . $this->db->datatype->matchPattern(array(1 => '_', 'o', '%'), 'LIKE', 'user_name');
            $values = $this->db->queryCol($query, 'text');
            $this->assertEquals($case_sensitive_expect, count($values), "case sensitive search was expected to return 2 rows but returned: ".count($values));
    
            $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE '
                . $this->db->datatype->matchPattern(array(1 => '_', 'o', '%'), 'ILIKE', 'user_name');
            $values = $this->db->queryCol($query, 'text');
            $this->assertEquals(3, count($values), "case insensitive search was expected to return 3 rows but returned: ".count($values));
        }
    
        /**
         * Tests escaping of text pattern strings with special characters
         * @dataProvider provider
         */
        public function testEscapePatternSequences($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('pattern_escaping')) {
                return;
            }
    
            $test_strings = array(
                "%",
                "_",
                "%_",
                "_%",
                "%Foo%",
                "%Foo_",
                "Foo%123",
                "Foo_123",
                "_Foo%",
                "_Foo_",
                "%'",
                "_'",
                "'%",
                "'_",
                "'%'",
                "'_'",
            );
    
            $this->clearTables();
            foreach($test_strings as $key => $string) {
                $value = $this->db->quote($string, 'text');
                $query = "INSERT INTO $this->table_users (user_name,user_id) VALUES ($value, $key)";
                $result = $this->db->exec($query);
                if (MDB2::isError($result)) {
                    $this->assertTrue(false, 'Error executing insert query'.$result->getMessage());
                }
    
                $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE user_name LIKE '.$this->db->quote($string, 'text', true, true);
                $value = $this->db->queryOne($query, 'text');
                if (MDB2::isError($value)) {
                    $this->assertTrue(false, 'Error executing select query'.$value->getMessage());
                }
    
                $this->assertEquals($string, $value, "the value retrieved for field \"user_name\" doesn't match what was stored");
            }
    
            $this->db->loadModule('Datatype', null, true);
            $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE user_name LIKE '.$this->db->datatype->matchPattern(array('Foo%', '_', '23'));
            $value = $this->db->queryOne($query, 'text');
            $this->assertEquals('Foo%123', $value, "the value retrieved for field \"user_name\" doesn't match what was stored");
    
            $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE user_name LIKE '.$this->db->datatype->matchPattern(array(1 => '_', 'oo', '%'));
            $value = $this->db->queryOne($query, 'text');
            $this->assertEquals('Foo', substr($value, 0, 3), "the value retrieved for field \"user_name\" doesn't match what was stored");
        }
    
        /**
         * A method to test that the MDB2_Driver_Datatype_Common::getValidTypes()
         * method returns the correct data array.
         * @dataProvider provider
         */
        public function testGetValidTypes($ci) {
            $this->manualSetUp($ci);
    
            $this->db->loadModule('Datatype', null, true);
            // Test with just the default MDB2 datatypes.
            $aExpected = $this->db->datatype->valid_default_values;
            $aResult = $this->db->datatype->getValidTypes();
            $this->assertEquals($aExpected, $aResult, 'getValidTypes');
    
            // Test with a custom datatype
            $this->db->setOption('datatype_map', array('test' => 'test'));
            $this->db->setOption('datatype_map_callback', array('test' => 'datatype_test_callback'));
            $aExpected = array_merge(
                $this->db->datatype->valid_default_values,
                array('test' => 'datatype_test_callback::getvalidtypes')
            );
            $aResult = $this->db->datatype->getValidTypes();
            $this->assertEquals($aExpected, $aResult, 'getValidTypes');
            unset($this->db->options['datatype_map']);
            unset($this->db->options['datatype_map_callback']);
    
        }
    
        /**
         * A method to test that the MDB2_Driver_Datatype_Common::convertResult()
         * method returns correctly converted column data.
         * @dataProvider provider
         */
        public function testConvertResult($ci) {
            $this->manualSetUp($ci);
    
            $this->db->loadModule('Datatype', null, true);
            // Test with an MDB2 datatype, eg. "text"
            $value = 'text';
            $type = 'text';
            $result = $this->db->datatype->convertResult($value, $type);
            $this->assertEquals($value, $result, 'convertResult');
    
            // Test with a custom datatype
            $this->db->setOption('datatype_map', array('test' => 'test'));
            $this->db->setOption('datatype_map_callback', array('test' => 'datatype_test_callback'));
            $value = 'text';
            $type = 'test';
            $result = $this->db->datatype->convertResult($value, $type);
    
            // Do this to avoid memory exhaustion by PHPUnit.
            if (MDB2::isError($result)) {
                $this->fail($result->getUserInfo());
            } else {
                $this->assertEquals('datatype_test_callback::convertresult', $result, 'mapPrepareDatatype');
            }
    
            unset($this->db->options['datatype_map']);
            unset($this->db->options['datatype_map_callback']);
        }
    
        /**
         * A method to test that the MDB2_Driver_Datatype_Common::getDeclaration()
         * method returns correctly formatted SQL for declaring columns.
         * @dataProvider provider
         */
        public function testGetDeclaration($ci) {
            $this->manualSetUp($ci);
    
            $this->db->loadModule('Datatype', null, true);
            // Test with an MDB2 datatype, eg. "integer"
            $name = 'column';
            $type = 'integer';
            $field = array('type' => 'integer');
            $result = $this->db->datatype->getDeclaration($type, $name, $field);
            $actual_type = $this->db->phptype == 'sqlite' ? 'INTEGER' : 'INT';
            $default = $this->db->phptype == 'mssql' ? ' NULL' : ' DEFAULT NULL';
    
            // Do this to avoid memory exhaustion by PHPUnit.
            if (MDB2::isError($result)) {
                $this->fail($result->getUserInfo());
            } else {
                $this->assertEquals('column '.$actual_type.$default, $result, 'getDeclaration');
            }
    
            // Test with a custom datatype
            $this->db->setOption('datatype_map', array('test' => 'test'));
            $this->db->setOption('datatype_map_callback', array('test' => 'datatype_test_callback'));
            $name = 'column';
            $type = 'test';
            $field = array('type' => 'test');
            $result = $this->db->datatype->getDeclaration($type, $name, $field);
    
            // Do this to avoid memory exhaustion by PHPUnit.
            if (MDB2::isError($result)) {
                $this->fail($result->getUserInfo());
            } else {
                $this->assertEquals('datatype_test_callback::getdeclaration', $result, 'getDeclaration');
            }
    
            // Test with a custom datatype without datatype_map_callback function #1
            $name = 'address';
            $type = 'text';
            $field = array(
                'name'    => 'company_addr',
                'type'    => 'text',
                'notnull' => 'true'
            );
            $this->db->setOption('datatype_map', array($name => $type));
    
            // Do this to avoid memory exhaustion by PHPUnit.
            if (MDB2::isError($result)) {
                $this->fail($result->getUserInfo());
            } else {
                $result = $this->db->datatype->getDeclaration($field['type'], $field['name'], $field);
            }
    
            $notnull = ' NOT NULL';
            $expected = $field['name'].' '.$this->db->datatype->getTypeDeclaration(array('type' => $type)).$notnull;
            $this->assertEquals($expected, $result);
    
            // Test with a custom datatype without datatype_map_callback function #2
            $name = 'address';
            $type = 'text';
            $field = array(
                'name' => 'company_addr',
                'type' => 'address',
            );
            $this->db->setOption('datatype_map', array($name => $type));
    
            // Do this to avoid memory exhaustion by PHPUnit.
            if (MDB2::isError($result)) {
                $this->fail($result->getUserInfo());
            } else {
                $result = $this->db->datatype->getDeclaration($field['type'], $field['name'], $field);
            }
    
            $default = $this->db->phptype == 'mssql' ? ' NULL' : '';
            $expected = $field['name'].' '.$this->db->datatype->getTypeDeclaration(array('type' => $type)).$default;
            $this->assertEquals($expected, $result);
            unset($this->db->options['datatype_map']);
            unset($this->db->options['datatype_map_callback']);
        }
    
        /**
         * A method to test that the MDB2_Driver_Datatype_Common::compareDefinition()
         * method
         * @dataProvider provider
         */
        public function testCompareDefinition($ci) {
            $this->manualSetUp($ci);
    
            // Test with an MDB2 datatype, eg. "text"
            $aPrevious = array(
                'type'   => 'text',
                'length' => 4
            );
            $aCurrent = array(
                'type'   => 'text',
                'length' => 5
            );
            $aResult = $this->db->datatype->compareDefinition($aCurrent, $aPrevious);
            $this->assertTrue(is_array($aResult), 'compareDefinition');
            $this->assertEquals(1, count($aResult), 'compareDefinition');
            $this->assertTrue($aResult['length'], 'compareDefinition');
    
            // Test with a custom datatype
            $this->db->setOption('datatype_map', array('test' => 'test'));
            $this->db->setOption('datatype_map_callback', array('test' => 'datatype_test_callback'));
            $aPrevious = array(
                'type'   => 'test'
            );
            $aCurrent = array(
                'type'   => 'test'
            );
            $result = $this->db->datatype->compareDefinition($aCurrent, $aPrevious);
    
            // Do this to avoid memory exhaustion by PHPUnit.
            if (MDB2::isError($result)) {
                $this->fail($result->getUserInfo());
            } else {
                $this->assertEquals('datatype_test_callback::comparedefinition', $result, 'compareDefinition');
            }
    
            unset($this->db->options['datatype_map']);
            unset($this->db->options['datatype_map_callback']);
        }
    
        /**
         * A method to test that the MDB2_Driver_Datatype_Common::quote()
         * method returns correctly quoted column data.
         * @dataProvider provider
         */
        public function testQuote($ci) {
            $this->manualSetUp($ci);
    
            $this->db->loadModule('Datatype', null, true);
            // Test with an MDB2 datatype, eg. "text"
            $value = 'text';
            $type = 'text';
            $result = $this->db->datatype->quote($value, $type);
            $this->assertEquals("'$value'", $result, 'quote');
    
            // Test with a custom datatype
            $this->db->setOption('datatype_map', array('test' => 'test'));
            $this->db->setOption('datatype_map_callback', array('test' => 'datatype_test_callback'));
            $value = 'text';
            $type = 'test';
            $result = $this->db->datatype->quote($value, $type);
    
            // Do this to avoid memory exhaustion by PHPUnit.
            if (MDB2::isError($result)) {
                $this->fail($result->getUserInfo());
            } else {
                $this->assertEquals('datatype_test_callback::quote', $result, 'quote');
            }
    
            unset($this->db->options['datatype_map']);
            unset($this->db->options['datatype_map_callback']);
        }
    
        /**
         * A method to test that the MDB2_Driver_Datatype_Common::mapPrepareDatatype()
         * method returns the correct data type.
         * @dataProvider provider
         */
        public function testMapPrepareDatatype($ci) {
            $this->manualSetUp($ci);
    
            $this->db->loadModule('Datatype', null, true);
            // Test with an MDB2 datatype, eg. "text"
            $type = 'text';
            $result = $this->db->datatype->mapPrepareDatatype($type);
            if ($this->db->phptype == 'mysqli') {
                $type = 's';
            }
            $this->assertEquals($type, $result, 'mapPrepareDatatype');
    
            // Test with a custom datatype
            $this->db->setOption('datatype_map', array('test' => 'test'));
            $this->db->setOption('datatype_map_callback', array('test' => 'datatype_test_callback'));
            $type = 'test';
            $result = $this->db->datatype->mapPrepareDatatype($type);
            $this->assertEquals('datatype_test_callback::mappreparedatatype', $result, 'mapPrepareDatatype');
            unset($this->db->options['datatype_map']);
            unset($this->db->options['datatype_map_callback']);
        }
    
        /**
         * A method to test that the MDB2_Driver_Datatype_Common::mapNativeDatatype()
         * method returns the correct MDB2 datatype from a given nativetype.
         * @dataProvider provider
         */
        public function testMapNativeDatatype($ci) {
            $this->manualSetUp($ci);
    
            $this->db->loadModule('Datatype', null, true);
            // Test with an common nativetype, eg. "text"
            $field = array(
                'type'   => 'int',
                'length' => 8
            );
            if (in_array($this->db->phptype, array('ibase', 'oci8'))) {
                $field['type'] = 'integer';
            }
            $expected_length = 8;
            if (in_array($this->db->phptype, array('mysql', 'mysqli', 'pgsql', 'sqlite', 'mssql'))) {
                $expected_length = 4;
            }
            $result = $this->db->datatype->mapNativeDatatype($field);
            if (MDB2::isError($result)) {
                $this->assertTrue(false, 'mapNativeDatatype: '.$result->getUserInfo());
            } else {
                $this->assertTrue(is_array($result), 'mapNativeDatatype');
                $this->assertEquals(4, count($result), 'mapNativeDatatype');
                $this->assertEquals('integer', $result[0][0], 'mapNativeDatatype');
                $this->assertEquals($expected_length, $result[1], 'mapNativeDatatype');
            }
    
            // Test with a custom nativetype mapping
            $this->db->setOption('nativetype_map_callback', array('test' => 'nativetype_test_callback'));
            $field = array(
                'type'   => 'test'
            );
            $result = $this->db->datatype->mapNativeDatatype($field);
            $this->assertTrue(is_array($result), 'mapNativeDatatype');
            $this->assertEquals(4, count($result), 'mapNativeDatatype');
            $this->assertEquals('test', $result[0][0], 'mapNativeDatatype');
            $this->assertNull($result[1], 'mapNativeDatatype');
            $this->assertNull($result[2], 'mapNativeDatatype');
            $this->assertNull($result[3], 'mapNativeDatatype');
            $field = array(
                'type'   => 'test(10)'
            );
            $result = $this->db->datatype->mapNativeDatatype($field);
            $this->assertTrue(is_array($result), 'mapNativeDatatype');
            $this->assertEquals(count($result), 4, 'mapNativeDatatype');
            $this->assertEquals($result[0][0], 'test', 'mapNativeDatatype');
            $this->assertEquals($result[1], 10, 'mapNativeDatatype');
            $this->assertNull($result[2], 'mapNativeDatatype');
            $this->assertNull($result[3], 'mapNativeDatatype');
            unset($this->db->options['nativetype_map_callback']);
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/ExtendedTest.php000066400000000000000000000725761213666021100227630ustar00rootroot00000000000000                                |
    // |          Lorenzo Alberton            |
    // +----------------------------------------------------------------------+
    //
    // $Id: ExtendedTest.php 327313 2012-08-27 15:16:42Z danielc $
    
    require_once dirname(__DIR__) . '/autoload.inc';
    
    class Standard_ExtendedTest extends Standard_Abstract
    {
        /**
         * @dataProvider provider
         */
        public function testAutoExecute($ci) {
            $this->manualSetUp($ci);
    
            $data = $this->getSampleData();
            $select_query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
    
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('Error loading "Extended" module: '.$result->getMessage());
            }
            $result = $this->db->extended->autoExecute($this->table_users, $data, MDB2_AUTOQUERY_INSERT, null, $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error auto executing insert: '.$result->getMessage());
            }
    
            $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
            $result = $this->db->query($select_query, $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: '.$result->getMessage());
            } else {
                $this->verifyFetchedValues($result, null, $data);
                $result->free();
            }
    
            $where = 'user_id = '.$this->db->quote($data['user_id'], 'integer');
            $result = $this->db->extended->autoExecute($this->table_users, null, MDB2_AUTOQUERY_SELECT, $where, null, true, $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error auto executing select: '.$result->getMessage());
            } else {
                $this->verifyFetchedValues($result, null, $data);
                $result->free();
            }
    
            $where = 'user_id = '.$this->db->quote($data['user_id'], 'integer');
            $result = $this->db->extended->autoExecute($this->table_users, null, MDB2_AUTOQUERY_SELECT, $where, null, true, MDB2_PREPARE_RESULT);
            if (MDB2::isError($result)) {
                $this->fail('Error auto executing select: '.$result->getMessage());
            } else {
                $result->setResultTypes($this->fields);
                $this->verifyFetchedValues($result, null, $data);
                $result->free();
            }
    
            $update_data = array();
            $data['user_name'] = $update_data['user_name'] = 'foo';
    
            $where = 'user_id = '.$this->db->quote($data['user_id'], 'integer');
            $result = $this->db->extended->autoExecute($this->table_users, $update_data, MDB2_AUTOQUERY_UPDATE, $where, $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error auto executing insert: '.$result->getMessage());
            }
    
            $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
            $result = $this->db->query($select_query, $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: '.$result->getMessage());
            } else {
                $this->verifyFetchedValues($result, null, $data);
                $result->free();
            }
    
            $where = array($where, 'user_name = '.$this->db->quote($data['user_name'], 'text'));
            $result = $this->db->extended->autoExecute($this->table_users, null, MDB2_AUTOQUERY_DELETE, $where, null);
            if (MDB2::isError($result)) {
                $this->fail('Error auto executing insert: '.$result->getMessage());
            }
    
            $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
            $result = $this->db->query($select_query, $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: '.$result->getMessage());
            } else {
                $this->assertEquals(0, $result->numRows(), 'No rows were expected to be returned');
                $result->free();
            }
        }
    
        /**
         * Test getAssoc()
         *
         * Test fetching two columns from a resultset. Return them as (key,value) pairs.
         * @dataProvider provider
         */
        public function testGetAssoc($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('Error loading "Extended" module: '.$result->getMessage());
            }
    
            $data = $this->getSampleData(1234);
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            $this->checkResultForErrors($stmt, 'prepare');
            $result = $stmt->execute(array_values($data));
            $stmt->free();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: '.$result->getMessage());
            }
    
            //test getAssoc() with query parameters
            $query = 'SELECT user_id, user_name FROM ' . $this->table_users . ' WHERE user_id=?';
            $result = $this->db->extended->getAssoc($query, array('integer', 'text'), array(1234), array('integer'));
            if (MDB2::isError($result)) {
                $this->fail('Error executing getAssoc(): '.$result->getMessage());
            }
            $this->assertTrue(array_key_exists($data['user_id'], $result), 'Unexpected returned key');
            $this->assertEquals($data['user_name'], $result[$data['user_id']], 'Unexpected returned value');
    
            //test getAssoc() without query parameters
            $query = 'SELECT user_id, user_name FROM ' . $this->table_users . ' WHERE user_id=1234';
            $result = $this->db->extended->getAssoc($query, array('integer', 'text'));
            if (MDB2::isError($result)) {
                $this->fail('Error executing getAssoc(): '.$result->getMessage());
            } else {
                $this->assertTrue(array_key_exists($data['user_id'], $result), 'Unexpected returned key');
                $this->assertEquals($data['user_name'], $result[$data['user_id']], 'Unexpected returned value');
            }
    
            //add another record to the db
            $data2 = $this->getSampleData(4321);
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            $result = $stmt->execute(array_values($data2));
            $stmt->free();
    
            //test getAssoc() with $force_array=true
            $query = 'SELECT user_id, user_name FROM ' . $this->table_users . ' ORDER BY user_id';
            $values = $this->db->extended->getAssoc($query, array('integer', 'text'), null, null, MDB2_FETCHMODE_ASSOC, true);
            if (MDB2::isError($values)) {
                $this->fail('Error executing getAssoc(): '.$values->getMessage());
            } else {
                $this->assertEquals(2, count($values), 'Error: incorrect number of returned rows');
                list($id, $value) = each($values);
                $this->assertEquals($data['user_id'],   $id,                 'Unexpected returned value');
                $this->assertEquals($data['user_name'], $value['user_name'], 'Unexpected returned value');
                list($id, $value) = each($values);
                $this->assertEquals($data2['user_id'],   $id,                 'Unexpected returned value');
                $this->assertEquals($data2['user_name'], $value['user_name'], 'Unexpected returned value');
            }
    
    
            //test getAssoc() with $force_array=false and $group=true
            $query = 'SELECT user_id, user_name FROM ' . $this->table_users . ' ORDER BY user_id';
            $values = $this->db->extended->getAssoc($query, array('integer', 'text'), null, null, MDB2_FETCHMODE_ASSOC, false, true);
            if (MDB2::isError($values)) {
                $this->fail('Error executing getAssoc(): '.$values->getMessage());
            } else {
                //@todo: check if MDB2_FETCHMODE_ASSOC is behaving correctly in this case
                $this->assertEquals(2, count($values), 'Error: incorrect number of returned rows');
                list($id, $value) = each($values);
                $this->assertEquals($data['user_id'],   $id,       'Unexpected returned value');
                $this->assertEquals($data['user_name'], $value[0], 'Unexpected returned value');
                list($id, $value) = each($values);
                $this->assertEquals($data2['user_id'],   $id,       'Unexpected returned value');
                $this->assertEquals($data2['user_name'], $value[0], 'Unexpected returned value');
    
            }
    
            //test $group=true with 3 fields
            $query = 'SELECT user_password, user_id, user_name FROM ' . $this->table_users . ' ORDER BY user_id';
            $values = $this->db->extended->getAssoc($query, array('integer', 'text', 'text'), null, null, MDB2_FETCHMODE_ASSOC, false, true);
            if (MDB2::isError($values)) {
                $this->fail('Error executing getAssoc(): '.$values->getMessage());
            } else {
                //the 2 values for user_password are equals, so they are collapsed in the same array
                $this->assertEquals(1, count($values), 'Error: incorrect number of returned rows');
                $values = array_shift($values);
                //there are 2 records
                $this->assertEquals(2, count($values), 'Error: incorrect number of returned rows');
                $value = array_shift($values);
                $this->assertEquals($data['user_id'],   $value['user_id'], 'Unexpected returned value');
                $this->assertEquals($data['user_name'], $value['user_name'], 'Unexpected returned value');
                $value = array_shift($values);
                $this->assertEquals($data2['user_id'],   $value['user_id'], 'Unexpected returned value');
                $this->assertEquals($data2['user_name'], $value['user_name'], 'Unexpected returned value');
            }
    
            //test $group=false with 3 fields
            $values = $this->db->extended->getAssoc($query, array('integer', 'text', 'text'), null, null, MDB2_FETCHMODE_ASSOC, false, false);
            if (MDB2::isError($values)) {
                $this->fail('Error executing getAssoc(): '.$values->getMessage());
            } else {
                //the 2 values for user_password are equals, so the first record is overwritten
                $this->assertEquals(1, count($values), 'Error: incorrect number of returned rows');
                $values = array_shift($values);
                $this->assertEquals($data2['user_id'],   $value['user_id'], 'Unexpected returned value');
                $this->assertEquals($data2['user_name'], $value['user_name'], 'Unexpected returned value');
            }
        }
    
        /**
         * Test getOne()
         *
         * Test fetching a single value
         * @dataProvider provider
         */
        public function testGetOne($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('Error loading "Extended" module: '.$result->getMessage());
            }
    
            $data = $this->getSampleData(1234);
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            $result = $stmt->execute(array_values($data));
            $stmt->free();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: '.$result->getMessage());
            }
    
            //test getOne() with query parameters
            $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE user_id=?';
            $result = $this->db->extended->getOne($query, 'text', array(1234), array('integer'));
            if (MDB2::isError($result)) {
                $this->fail('Error executing getOne(): '.$result->getMessage());
            }
            $this->assertEquals($data['user_name'], $result, 'Unexpected returned value');
    
            //test getOne() without query parameters
            $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE user_id=1234';
            $result = $this->db->extended->getOne($query, 'text');
            if (MDB2::isError($result)) {
                $this->fail('Error executing getOne(): '.$result->getMessage());
            }
            $this->assertEquals($data['user_name'], $result, 'Unexpected returned value');
    
            //test getOne() with column number (resultset: 0-based array)
            $query = 'SELECT user_id, user_name, approved FROM ' . $this->table_users . ' WHERE user_id=1234';
            $result = $this->db->extended->getOne($query, 'text', null, null, 1); //get the 2nd column
            if (MDB2::isError($result)) {
                $this->fail('Error executing getOne(): '.$result->getMessage());
            }
            $this->assertEquals($data['user_name'], $result, 'Unexpected returned value');
        }
    
        /**
         * Test getCol()
         *
         * Test fetching a column of result data.
         * @dataProvider provider
         */
        public function testGetCol($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('Error loading "Extended" module: '.$result->getMessage());
            }
    
            $data = array(
                0 => $this->getSampleData(1234),
                1 => $this->getSampleData(4321),
            );
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            $result = $stmt->execute(array_values($data[0]));
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: '.$result->getMessage());
            }
            $result = $stmt->execute(array_values($data[1]));
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: '.$result->getMessage());
            }
            $stmt->free();
    
            //test getCol() with query parameters
            $query = 'SELECT user_name FROM ' . $this->table_users . ' WHERE user_id>?';
            $result = $this->db->extended->getCol($query, 'text', array(1), array('integer'));
            if (MDB2::isError($result)) {
                $this->fail('Error executing getCol(): '.$result->getMessage());
            }
            $expected = array(
                $data[0]['user_name'],
                $data[1]['user_name'],
            );
            $this->assertEquals($expected, $result, 'Unexpected returned value');
    
            //test getCol() without query parameters
            $query = 'SELECT user_name FROM ' . $this->table_users;
            $result = $this->db->extended->getCol($query, 'text');
            if (MDB2::isError($result)) {
                $this->fail('Error executing getCol(): '.$result->getMessage());
            }
            $this->assertEquals($expected, $result, 'Unexpected returned value');
    
            //test getCol() with column number (resultset: 0-based array)
            $query = 'SELECT user_id, user_name, approved FROM ' . $this->table_users;
            $result = $this->db->extended->getCol($query, 'text', null, null, 1); //get the 2nd column
            if (MDB2::isError($result)) {
                $this->fail('Error executing getCol(): '.$result->getMessage());
            }
            $this->assertEquals($expected, $result, 'Unexpected returned value');
        }
    
        /**
         * Test getRow()
         *
         * Test fetching a row of result data.
         * @dataProvider provider
         */
        public function testGetRow($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('Error loading "Extended" module: '.$result->getMessage());
            }
    
            $data = $this->getSampleData(1234);
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            $result = $stmt->execute(array_values($data));
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: '.$result->getMessage());
            }
            $stmt->free();
    
            //test getRow() with query parameters
            $query = 'SELECT user_id, user_name, user_password FROM ' . $this->table_users . ' WHERE user_id=?';
            $result = $this->db->extended->getRow($query, array('integer', 'text', 'text'), array(1234), array('integer'), MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($result)) {
                $this->fail('Error executing getRow(): '.$result->getMessage());
            }
            $this->assertEquals($data['user_id'],       $result['user_id'],       'Unexpected returned value');
            $this->assertEquals($data['user_name'],     $result['user_name'],     'Unexpected returned value');
            $this->assertEquals($data['user_password'], $result['user_password'], 'Unexpected returned value');
    
            //test getRow() without query parameters
            $query = 'SELECT user_id, user_name, user_password FROM ' . $this->table_users;
            $result = $this->db->extended->getRow($query, array('integer', 'text', 'text'), null, null, MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($result)) {
                $this->fail('Error executing getRow(): '.$result->getMessage());
            }
            $this->assertEquals($data['user_id'],       $result['user_id'],       'Unexpected returned value');
            $this->assertEquals($data['user_name'],     $result['user_name'],     'Unexpected returned value');
            $this->assertEquals($data['user_password'], $result['user_password'], 'Unexpected returned value');
        }
    
        /**
         * Test getAll()
         *
         * Test fetching result data all at once.
         * @dataProvider provider
         */
        public function testGetAll($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('Error loading "Extended" module: '.$result->getMessage());
            }
    
            $data = array();
            $total_rows = 5;
    
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')', array_values($this->fields), MDB2_PREPARE_MANIP);
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: '.$result->getMessage());
                }
            }
            $stmt->free();
    
            //test getAll() with query parameters
            $query = 'SELECT user_id, user_name, user_password FROM ' . $this->table_users . ' WHERE user_id > ? ORDER BY user_id';
            $values = $this->db->extended->getAll($query, array('integer', 'text', 'text'), array(2), array('integer'), MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($values)) {
                $this->fail('Error fetching the result set: '.$values->getMessage());
            } else {
                $this->assertEquals(2, count($values), 'Error: incorrect number of returned rows');
                for ($i=0; $i<2; $i++) {
                    $this->assertEquals($data[$i+3]['user_id'],       $values[$i]['user_id'],       'Unexpected returned value');
                    $this->assertEquals($data[$i+3]['user_name'],     $values[$i]['user_name'],     'Unexpected returned value');
                    $this->assertEquals($data[$i+3]['user_password'], $values[$i]['user_password'], 'Unexpected returned value');
                }
            }
    
            //test getAll() without query parameters
            $query = 'SELECT user_id, user_name, user_password FROM ' . $this->table_users . ' ORDER BY user_id';
            $values = $this->db->extended->getAll($query, array('integer', 'text', 'text'), null, null, MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($values)) {
                $this->fail('Error fetching the result set: '.$values->getMessage());
            } else {
                $this->assertEquals($total_rows, count($values), 'Error: incorrect number of returned rows');
                for ($i=0; $i<$total_rows; $i++) {
                    $this->assertEquals($data[$i]['user_id'],       $values[$i]['user_id'],       'Unexpected returned value');
                    $this->assertEquals($data[$i]['user_name'],     $values[$i]['user_name'],     'Unexpected returned value');
                    $this->assertEquals($data[$i]['user_password'], $values[$i]['user_password'], 'Unexpected returned value');
                }
            }
        }
    
        /**
         * Test limitQuery()
         *
         * Test fetching a limited resultset.
         * @dataProvider provider
         */
        public function testLimitQuery($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('Error loading "Extended" module: '.$result->getMessage());
            }
    
            $data = array();
            $total_rows = 5;
    
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')', array_values($this->fields), MDB2_PREPARE_MANIP);
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: '.$result->getMessage());
                }
            }
            $stmt->free();
    
            $query = 'SELECT user_id, user_name, user_password FROM ' . $this->table_users . ' ORDER BY user_id';
            $types = array('integer', 'text', 'text');
    
            //test limitQuery() with offset = 0
            $result = $this->db->extended->limitQuery($query, $types, 2);
            if (MDB2::isError($result)) {
                $this->fail('Error executing limitQuery(): '.$result->getMessage());
            }
            $values = $result->fetchAll(MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($values)) {
                $this->fail('Error fetching the result set');
            } else {
                $this->assertEquals(2, count($values), 'Error: incorrect number of returned rows');
                for ($i=0; $i<2; $i++) {
                    $this->assertEquals($data[$i]['user_id'],       $values[$i]['user_id'],       'Unexpected returned value');
                    $this->assertEquals($data[$i]['user_name'],     $values[$i]['user_name'],     'Unexpected returned value');
                    $this->assertEquals($data[$i]['user_password'], $values[$i]['user_password'], 'Unexpected returned value');
                }
            }
            $result->free();
    
            //test limitQuery() with offset > 0
            $result = $this->db->extended->limitQuery($query, $types, 3, 2);
            if (MDB2::isError($result)) {
                $this->fail('Error executing limitQuery(): '.$result->getMessage());
            }
            $values = $result->fetchAll(MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($values)) {
                $this->fail('Error fetching the result set');
            } else {
                $this->assertEquals(3, count($values), 'Error: incorrect number of returned rows');
                for ($i=0; $i<3; $i++) {
                    $this->assertEquals($data[$i+2]['user_id'],       $values[$i]['user_id'],       'Unexpected returned value');
                    $this->assertEquals($data[$i+2]['user_name'],     $values[$i]['user_name'],     'Unexpected returned value');
                    $this->assertEquals($data[$i+2]['user_password'], $values[$i]['user_password'], 'Unexpected returned value');
                }
            }
            $result->free();
        }
    
        /**
         * Test execParam()
         *
         * Test executing a query with parameters
         * @dataProvider provider
         */
        public function testExecParam($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('Error loading "Extended" module: '.$result->getMessage());
            }
    
            $data = $this->getSampleData(1234);
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
    
            $result = $this->db->extended->execParam($query, array_values($data), $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error executing execParam(): '.$result->getMessage());
            }
    
            $query = 'SELECT user_id, user_name, user_password FROM ' . $this->table_users . ' WHERE user_id=?';
            $result = $this->db->extended->getRow($query, array('integer', 'text', 'text'), array(1234), array('integer'), MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($result)) {
                $this->fail('Error executing getRow(): '.$result->getMessage());
            }
            $this->assertEquals($data['user_id'],       $result['user_id'],       'Unexpected returned value');
            $this->assertEquals($data['user_name'],     $result['user_name'],     'Unexpected returned value');
            $this->assertEquals($data['user_password'], $result['user_password'], 'Unexpected returned value');
        }
    
        /**
         * Test executeMultiple()
         *
         * Test executing multiple prepared queries
         * @dataProvider provider
         */
        public function testExecuteMultiple($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->loadModule('Extended');
            if (MDB2::isError($result)) {
                $this->fail('Error loading "Extended" module: '.$result->getMessage());
            }
    
            $data = array();
            $total_rows = 5;
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = array_values($this->getSampleData($row));
            }
    
            $stmt = $this->db->prepare('INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')');
            $result = $this->db->extended->executeMultiple($stmt, $data);
            if (MDB2::isError($result)) {
                $this->fail('Error executing executeMultiple(): '.$result->getMessage().' :: '.$result->getUserInfo());
            }
            $stmt->free();
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $values = $this->db->queryAll($query, array_values($this->fields), MDB2_FETCHMODE_ORDERED);
    
            $n_fields = count($this->fields);
            for ($i=0; $i<$total_rows; $i++) {
                for ($field=0; $field<$n_fields; $field++) {
                    $this->assertEquals(strval($data[$i][$field]), strval($values[$i][$field]), 'Unexpected returned value');
                }
            }
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/FunctionTest.php000066400000000000000000000327541213666021100230020ustar00rootroot00000000000000            |
    // +----------------------------------------------------------------------+
    //
    // $Id: FunctionTest.php 322076 2012-01-11 15:51:09Z danielc $
    
    require_once dirname(__DIR__) . '/autoload.inc';
    
    class Standard_FunctionTest extends Standard_Abstract
    {
        /**
         * Can not use setUp() because we are using a dataProvider to get multiple
         * MDB2 objects per test.
         *
         * @param array $ci  an associative array with two elements.  The "dsn"
         *                   element must contain an array of DSN information.
         *                   The "options" element must be an array of connection
         *                   options.
         */
        protected function manualSetUp($ci) {
            parent::manualSetUp($ci);
    
            $this->db->loadModule('Function', null, true);
        }
    
        /**
         * Test functionTable()
         * @dataProvider provider
         */
        public function testFunctionTable($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'functionTable')) {
                return;
            }
    
            $functionTable_clause = $this->db->function->functionTable();
            $query = 'SELECT 1 '.$functionTable_clause;
            $result = $this->db->queryOne($query);
            if (MDB2::isError($result)) {
                $this->fail('Error fetching from function table: '.$result->getMessage().' :: '.$result->getUserInfo());
            } else {
                $this->assertEquals('1', $result, 'Error fetching value from function table');
            }
        }
    
        /**
         * Test now()
         * @dataProvider provider
         */
        public function testNow($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'now')) {
                return;
            }
    
            $tests = array(
                'timestamp' => '/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/',
                'date' => '/^\d{4}-\d{2}-\d{2}$/',
                'time' => '/^\d{2}:\d{2}:\d{2}$/',
            );
    
            foreach ($tests as $type => $regexp) {
                $functionTable_clause = $this->db->function->functionTable();
                $now_clause = $this->db->function->now($type);
                $query = 'SELECT '.$now_clause . $functionTable_clause;
                $result = $this->db->queryOne($query, $type);
                if (MDB2::isError($result)) {
                    $this->fail('Error getting '.$type);
                } else {
                    $this->assertRegExp($regexp, $result, 'Error: not a proper '.$type);
                }
            }
        }
    
        /**
         * Test substring()
         * @dataProvider provider
         */
        public function testSubstring($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'substring')) {
                return;
            }
            $data = $this->getSampleData(1234);
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
    
            $result = $stmt->execute(array_values($data));
            $stmt->free();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query'.$result->getMessage());
            }
    
            $substring_clause = $this->db->function->substring('user_name', 1, 4);
            $query = 'SELECT '.$substring_clause .' FROM ' . $this->table_users;
            $result = $this->db->queryOne($query);
            if (MDB2::isError($result)) {
                $this->fail('Error getting substring');
            } else {
                $this->assertEquals('user', $result, 'Error: substrings not equals');
            }
    
            $substring_clause = $this->db->function->substring('user_name', 5, 1);
            $query = 'SELECT '.$substring_clause .' FROM ' . $this->table_users;
            $result = $this->db->queryOne($query);
            if (MDB2::isError($result)) {
                $this->fail('Error getting substring');
            } else {
                $this->assertEquals('_', $result, 'Error: substrings not equals');
            }
    
            //test NULL 2nd parameter
            $substring_clause = $this->db->function->substring('user_name', 6);
            $query = 'SELECT '.$substring_clause .' FROM ' . $this->table_users;
            $result = $this->db->queryOne($query);
            if (MDB2::isError($result)) {
                $this->fail('Error getting substring');
            } else {
                $this->assertEquals('1234', $result, 'Error: substrings not equals');
            }
        }
    
        /**
         * Test concat()
         * @dataProvider provider
         */
        public function testConcat($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'concat')) {
                return;
            }
    
            $functionTable_clause = $this->db->function->functionTable();
            $concat_clause = $this->db->function->concat($this->db->quote('time', 'text'), $this->db->quote('stamp', 'text'));
            $query = 'SELECT '.$concat_clause . $functionTable_clause;
            $result = $this->db->queryOne($query);
            if (MDB2::isError($result)) {
                $this->fail('Error getting concat');
            } else {
                $this->assertEquals('timestamp', $result, 'Error: could not concatenate "time+stamp"');
            }
        }
    
        /**
         * Test random()
         * @dataProvider provider
         */
        public function testRandom($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'random')) {
                return;
            }
    
            $rand_clause = $this->db->function->random();
            $functionTable_clause = $this->db->function->functionTable();
            $query = 'SELECT '.$rand_clause . $functionTable_clause;
            $result = $this->db->queryOne($query, 'float');
            if (MDB2::isError($result)) {
                $this->fail('Error getting random value:'. $result->getMessage());
            } else {
                $this->assertTrue(($result >= 0 && $result <= 1), 'Error: could not get random value between 0 and 1: '.$result);
            }
        }
    
        /**
         * Test lower()
         * @dataProvider provider
         */
        public function testLower($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'lower')) {
                return;
            }
            $string = $this->db->quote('FoO');
            $lower_clause = $this->db->function->lower($string);
            $functionTable_clause = $this->db->function->functionTable();
            $query = 'SELECT '.$lower_clause . $functionTable_clause;
            $result = $this->db->queryOne($query, 'text');
            if (MDB2::isError($result)) {
                $this->fail('Error getting lower case value:'. $result->getMessage());
            } else {
                $this->assertTrue(($result === 'foo'), 'Error: could not lower case "FoO": '.$result);
            }
        }
    
        /**
         * Test upper()
         * @dataProvider provider
         */
        public function testUpper($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'upper')) {
                return;
            }
            $string = $this->db->quote('FoO');
            $upper_clause = $this->db->function->upper($string);
            $functionTable_clause = $this->db->function->functionTable();
            $query = 'SELECT '.$upper_clause . $functionTable_clause;
            $result = $this->db->queryOne($query, 'text');
            if (MDB2::isError($result)) {
                $this->fail('Error getting upper case value:'. $result->getMessage());
            } else {
                $this->assertTrue(($result === 'FOO'), 'Error: could not upper case "FoO": '.$result);
            }
        }
    
        /**
         * Test length()
         * @dataProvider provider
         */
        public function testLenght($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'length')) {
                return;
            }
            $string = $this->db->quote('foo');
            $length_clause = $this->db->function->length($string);
            $functionTable_clause = $this->db->function->functionTable();
            $query = 'SELECT '.$length_clause . $functionTable_clause;
            $len = $this->db->queryOne($query, 'integer');
            if (MDB2::isError($len)) {
                $this->fail('Error getting upper case value:'. $len->getMessage());
            } else {
                $this->assertEquals(3, $len, 'Error: incorrect length for "foo" string: '.$len);
            }
        }
    
        /**
         * Test replace()
         * @dataProvider provider
         */
        public function testReplace($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'replace')) {
                return;
            }
    
            $string  = $this->db->quote('so what');
            $search  = $this->db->quote('o');
            $replace = $this->db->quote('ay');
            $this->db->pushErrorHandling(PEAR_ERROR_RETURN);
            $this->db->expectError(MDB2_ERROR_UNSUPPORTED);
            $replace_clause = $this->db->function->replace($string, $search, $replace);
            $this->db->popExpect();
            $this->db->popErrorHandling();
            if (MDB2::isError($replace_clause) && $replace_clause->getCode() == MDB2_ERROR_UNSUPPORTED) {
                return;
            }
    
            $functionTable_clause = $this->db->function->functionTable();
            $query = 'SELECT '.$replace_clause . $functionTable_clause;
            $result = $this->db->queryOne($query, 'text');
            if (MDB2::isError($result)) {
                $this->fail('Error getting replaced value:'. $result->getMessage() . ' :: ' . $result->getUserInfo());
            } else {
                $this->assertEquals('say what', $result, 'Error: could not get replace string: '.$result);
            }
        }
    
        /**
         * Test unixtimestamp()
         * @dataProvider provider
         */
        public function testUnixtimestamp($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->function, 'unixtimestamp')) {
                return;
            }
    
            $datetime = '2008-01-01 00:00:00';
            $expected = 1199163600;
    
            $quoted_dt = $this->db->quote($datetime, 'timestamp');
            $this->db->pushErrorHandling(PEAR_ERROR_RETURN);
            $this->db->expectError(MDB2_ERROR_UNSUPPORTED);
            $unixts_clause = $this->db->function->unixtimestamp($quoted_dt);
            $this->db->popExpect();
            $this->db->popErrorHandling();
            if (MDB2::isError($unixts_clause) && $unixts_clause->getCode() == MDB2_ERROR_UNSUPPORTED) {
                return;
            }
    
            $functionTable_clause = $this->db->function->functionTable();
            $query = 'SELECT '.$unixts_clause . $functionTable_clause;
            $result = $this->db->queryOne($query, 'text');
            if (MDB2::isError($result)) {
                $this->fail('Error getting UNIX timestamp: ' . $result->getUserInfo());
            }
            $this->assertEquals($expected, $result);
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/InternalsTest.php000066400000000000000000001003001213666021100231330ustar00rootroot00000000000000                    |
    // |         Andrew Hill                         |
    // +----------------------------------------------------------------------+
    //
    // $Id: InternalsTest.php 328183 2012-10-29 15:10:42Z danielc $
    
    require_once dirname(__DIR__) . '/autoload.inc';
    
    class Standard_InternalsTest extends Standard_Abstract {
    
        public $clear_tables = false;
    
        /**
         * Tests that the MDB2::apiVersion() method returns an API version number.
         * @dataProvider provider
         */
        public function test_apiVersion($ci) {
            $this->manualSetUp($ci);
    
            $result = MDB2::apiVersion();
            if ('@'.'package_version'.'@' == '2.5.0b5') {
                $this->assertEquals('@'.'package_version'.'@', $result);
                return;
            }
            $this->assertNotNull($result, 'apiVersion null: '.$result);
            $result = strtok($result, '.');
            $this->assertTrue(is_numeric($result), 'apiVersion major: '.$result);
            $result = strtok('.');
            $this->assertTrue(is_numeric($result), 'apiVersion minor: '.$result);
            $result = strtok('.');
            $this->assertRegExp('/\d+((b|a|pl|rc)\d+)?/', $result);
        }
    
        /**
         * Tests that the MDB2::classExists() method correctly tests for
         * existence of a class.
         * @dataProvider provider
         */
        public function test_classExists($ci) {
            $this->manualSetUp($ci);
    
            $this->assertFalse(MDB2::classExists('null'), 'classExists');
            $this->assertTrue(MDB2::classExists('MDB2'), 'classExists');
        }
    
        /**
         * Tests that the MDB2::loadClass() method correctly loads classes.
         * @dataProvider provider
         */
        public function test_loadClass($ci) {
            $this->manualSetUp($ci);
    
            $this->assertTrue(MDB2::loadClass('MDB2', false), 'loadClass');
            // Suppress handling of PEAR errors while testing next case
            $this->db->pushErrorHandling(null);
            $result = MDB2::loadClass('null', false);
            $this->assertTrue(is_object($result) && is_a($result, 'pear_error'), 'loadClass');
            $this->db->popErrorHandling();
        }
    
        /**
         * Tests that the MDB2::factory() method correctly connects to a
         * database.
         * @dataProvider provider
         */
        public function test_factory($ci) {
            $this->manualSetUp($ci);
    
            $db = MDB2::factory($this->dsn);
            $this->assertTrue(MDB2::isConnection($db), 'factory');
            // Suppress handling of PEAR errors while preparing the
            // next test case database connection
            $this->db->pushErrorHandling(null);
            $db = MDB2::factory(null);
            $this->db->popErrorHandling();
            $this->assertFalse(MDB2::isConnection($db), 'factory');
        }
    
        /**
         * Tests that the MDB2::loadFile() method returns the expected
         * filename.
         * @dataProvider provider
         */
        public function test_loadFile($ci) {
            $this->manualSetUp($ci);
    
            $filename = 'Extended';
            $this->assertEquals('MDB2'.DIRECTORY_SEPARATOR.$filename.'.php', MDB2::loadFile($filename), 'loadFile');
        }
    
        /**
         * Tests that the MDB2::isConnection() method correctly reports
         * connections.
         * @dataProvider provider
         */
        public function test_isConnection($ci) {
            $this->manualSetUp($ci);
    
            $this->assertTrue(MDB2::isConnection($this->db), 'isConnection');
            $this->assertFalse(MDB2::isConnection(null), 'isConnection');
        }
    
        /**
         * Tests that the MDB2::isResult() method correctly identifies
         * results.
         * @dataProvider provider
         */
        public function test_isResult($ci) {
            $this->manualSetUp($ci);
    
            $obj = new MDB2_Result();
            $this->assertTrue(MDB2::isResult($obj), 'isResult');
            $obj = null;
            $this->assertFalse(MDB2::isResult($obj), 'isResult');
        }
    
        /**
         * Tests that the MDB2::isResultCommon() method correctly identifies
         * common results.
         * @dataProvider provider
         */
        public function test_isResultCommon($ci) {
            $this->manualSetUp($ci);
    
            $result = null;
            $obj = new MDB2_Result_Common($this->db, $result);
            $this->assertTrue(MDB2::isResultCommon($obj), 'isResultCommon');
            $obj = null;
            $this->assertFalse(MDB2::isResultCommon($obj), 'isResultCommon');
        }
    
        /**
         * Tests that the MDB2::parseDSN() method works.
         * @dataProvider provider
         */
        public function test_parseDSN($ci) {
            $this->manualSetUp($ci);
    
            $dsn = $this->dsn;
            $result = MDB2::parseDSN($dsn);
            $this->assertEquals($dsn['phptype'],$result['dbsyntax'],'parseDSN');
    
            $dsn = "mydbms://myname:mypassword@localhost";
            $result = MDB2::parseDSN($dsn);
            $this->assertEquals('mydbms', $result['phptype'],'parseDSN');
            $this->assertEquals('mydbms',$result['dbsyntax'],'parseDSN');
            $this->assertEquals('tcp',$result['protocol'],'parseDSN');
            $this->assertEquals('localhost',$result['hostspec'],'parseDSN');
            $this->assertEquals(false,$result['port'],'parseDSN');
            $this->assertEquals(false,$result['socket'],'parseDSN');
            $this->assertEquals('myname',$result['username'],'parseDSN');
            $this->assertEquals('mypassword',$result['password'],'parseDSN');
            $this->assertEquals(false,$result['database'],'parseDSN');
    
            $dsn = "somesql://myname:mypassword@localhost:1234/mydb";
            $result = MDB2::parseDSN($dsn);
            $this->assertEquals('somesql',$result['phptype'],'parseDSN');
            $this->assertEquals('somesql',$result['dbsyntax'],'parseDSN');
            $this->assertEquals('tcp',$result['protocol'],'parseDSN');
            $this->assertEquals('localhost',$result['hostspec'],'parseDSN');
            $this->assertEquals('1234',$result['port'],'parseDSN');
            $this->assertEquals(false,$result['socket'],'parseDSN');
            $this->assertEquals('myname',$result['username'],'parseDSN');
            $this->assertEquals('mypassword',$result['password'],'parseDSN');
            $this->assertEquals('mydb',$result['database'],'parseDSN');
    
            $dsn = "dbms1://myname@unix(opts)/mydb?param1=value1";
            $result = MDB2::parseDSN($dsn);
            $this->assertEquals('dbms1',$result['phptype'],'parseDSN');
            $this->assertEquals('dbms1',$result['dbsyntax'],'parseDSN');
            $this->assertEquals('unix',$result['protocol'],'parseDSN');
            $this->assertEquals(false,$result['hostspec'],'parseDSN');
            $this->assertEquals(false,$result['port'],'parseDSN');
            $this->assertEquals('opts',$result['socket'],'parseDSN');
            $this->assertEquals('myname',$result['username'],'parseDSN');
            $this->assertEquals(false,$result['password'],'parseDSN');
            $this->assertEquals('mydb',$result['database'],'parseDSN');
            $this->assertEquals('value1',$result['param1'],'parseDSN');
        }
    
        /**
         * Tests that the MDB2::fileExists() method correctly identifies
         * existing/non-existing files.
         * @dataProvider provider
         */
        public function test_fileExists($ci) {
            $this->manualSetUp($ci);
    
            $this->assertTrue(MDB2::fileExists('PEAR.php'), 'fileExists');
            $this->assertFalse(MDB2::fileExists('itIsHopedThatNoOneHasAFileWithThisName.php'), 'fileExists');
        }
    
        /**
         * Tests that the MDB2::__toString() method returns the expected
         * string result.
         * @dataProvider provider
         */
        public function test__toString($ci) {
            $this->manualSetUp($ci);
    
            $expected = "MDB2_Driver_{$this->dsn['phptype']}: (phptype = {$this->dsn['phptype']}, dbsyntax = {$this->db->dbsyntax})";
            switch ($this->db->phptype) {
                case 'sqlite':
                    $expected .= ' [connected]';
                    break;
            }
            if (version_compare(PHP_VERSION, "5.0.0", "<")) {
                $expected = strtolower($expected);
            }
            $this->assertEquals($expected ,$this->db->__toString(), '__toString');
        }
    
        /**
         * Tests that the MDB2::setFetchMode() method correctly sets the
         * fetch mode.
         * @dataProvider provider
         */
        public function test_setFetchMode($ci) {
            $this->manualSetUp($ci);
    
            $tmp = $this->db->fetchmode;
            $this->db->setFetchMode(MDB2_FETCHMODE_OBJECT);
            $this->assertEquals('stdClass', $this->db->options['fetch_class'], 'setFetchMode');
            $this->db->setFetchMode(MDB2_FETCHMODE_ORDERED);
            $this->assertEquals(MDB2_FETCHMODE_ORDERED, $this->db->fetchmode, 'setFetchMode');
            $this->db->setFetchMode(MDB2_FETCHMODE_ASSOC);
            $this->assertEquals(MDB2_FETCHMODE_ASSOC, $this->db->fetchmode, 'setFetchMode');
            $this->db->fetchmode = $tmp;
        }
    
        /**
         * Tests that the MDB2::escape() method correctly escapes strings.
         * @dataProvider provider
         */
        public function test_escape($ci) {
            $this->manualSetUp($ci);
            $text = "xxx'z'xxx";
            switch ($this->db->phptype) {
                case 'mysql':
                case 'mysqli':
                    $expect = "xxx\'z\'xxx";
                    break;
                default:
                    $expect = "xxx''z''xxx";
            }
            $this->assertEquals($expect, $this->db->escape($text), 'escape');
        }
    
        /**
         * Tests that the MDB2::quoteIdentifier() method correctly quotes strings.
         * @dataProvider provider
         */
        public function test_quoteIdentifier($ci) {
            $this->manualSetUp($ci);
    
            if ($this->db->phptype == 'ibase') {
                return;
            }
            $tmp = $this->db->identifier_quoting;
            $this->db->identifier_quoting['start'] = '"';
            $this->db->identifier_quoting['end'] = '`';
            $this->db->identifier_quoting['escape'] = '/';
            $text = 'my`identifier';
            $this->assertEquals('"my/`identifier`', $this->db->quoteIdentifier($text), 'quoteIdentifier');
            $this->db->identifier_quoting = $tmp;
        }
    
        /**
         * Tests that the MDB2::getAsKeyword() method correctly returns
         * the set "as" keyword.
         * @dataProvider provider
         */
        public function test_getAsKeyword($ci) {
            $this->manualSetUp($ci);
    
            $tmp = $this->db->as_keyword;
            $this->db->as_keyword = 'ALIAS';
            $this->assertEquals('ALIAS', $this->db->getAsKeyword(), 'getAsKeyword');
            $this->db->as_keyword = $tmp;
        }
    
        /**
         * Tests that the MDB2::getConnection() method correctly returns
         * a database resource.
         * @dataProvider provider
         */
        public function test_getConnection($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->getConnection();
            $this->assertTrue(is_resource($result) || is_object($result), 'getConnection');
        }
    
        /**
         * A private method to return a defined "row" of data for use
         * in the next set of tests.
         *
         * @access private
         * @return array The array of "row" data.
         * @dataProvider provider
         */
        public function _fetchRowData()
        {
            return array(
                0         => '',
                1         => 'notnull',
                2         => 'length7   ',
                '1?2:3.4' => 'assoc'
            );
        }
    
        /**
         * A private method to test results from the MDB2::_fixResultArrayValues()
         * method when the $mode parameter was set to MDB2_PORTABILITY_EMPTY_TO_NULL.
         *
         * @access private
         * @param array $row The result of the call to MDB2::_fixResultArrayValues().
         * @dataProvider provider
         */
        public function _fixResultArrayValues_Test_EmptyToNull($row)
        {
            $this->assertNull($row[0], '_fixResultArrayValues');
            $this->assertNotNull($row[1], '_fixResultArrayValues');
            $this->assertNotNull($row[2], '_fixResultArrayValues');
        }
    
        /**
         * A private method to test results from the MDB2::_fixResultArrayValues()
         * method when the $mode parameter was set to MDB2_PORTABILITY_RTRIM.
         *
         * @access private
         * @param array $row The result of the call to MDB2::_fixResultArrayValues().
         * @dataProvider provider
         */
        public function _fixResultArrayValues_Test_Rtrim($row)
        {
            $this->assertEquals(strlen($row[0]), 0, '_fixResultArrayValues');
            $this->assertEquals(strlen($row[1]), 7, '_fixResultArrayValues');
            $this->assertEquals(strlen($row[2]), 7, '_fixResultArrayValues');
        }
    
        /**
         * A private method to test results from the MDB2::_fixResultArrayValues()
         * method when the $mode parameter was set to MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES.
         *
         * @access private
         * @param array $row The result of the call to MDB2::_fixResultArrayValues().
         * @dataProvider provider
         */
        public function _fixResultArrayValues_Test_FixAssocFieldNames($row)
        {
            $this->assertTrue(array_key_exists(4, $row), '_fixResultArrayValues');
            $this->assertTrue($row[4] == 'assoc', '_fixResultArrayValues');
        }
    
        /**
         * Tests that the MDB2::_fixResultArrayValues() method fixes array
         * values when used with various $mode parameters.
         * @dataProvider provider
         */
        public function test__fixResultArrayValues($ci) {
            $this->manualSetUp($ci);
    
            $mode = MDB2_PORTABILITY_EMPTY_TO_NULL;
            $row = $this->_fetchRowData();
            $this->db->_fixResultArrayValues($row, $mode);
            $this->_fixResultArrayValues_Test_EmptyToNull($row);
    
            $mode = MDB2_PORTABILITY_RTRIM;
            $row = $this->_fetchRowData();
            $this->db->_fixResultArrayValues($row, $mode);
            $this->_fixResultArrayValues_Test_Rtrim($row);
    
            $mode = MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES;
            $row = $this->_fetchRowData();
            $this->db->_fixResultArrayValues($row, $mode);
            $this->_fixResultArrayValues_Test_FixAssocFieldNames($row);
    
            $mode = MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_RTRIM;
            $row = $this->_fetchRowData();
            $this->db->_fixResultArrayValues($row, $mode);
            $this->_fixResultArrayValues_Test_EmptyToNull($row);
            $this->_fixResultArrayValues_Test_Rtrim($row);
    
            $mode = MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES;
            $row = $this->_fetchRowData();
            $this->db->_fixResultArrayValues($row, $mode);
            $this->_fixResultArrayValues_Test_EmptyToNull($row);
            $this->_fixResultArrayValues_Test_FixAssocFieldNames($row);
    
            $mode = MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES;
            $row = $this->_fetchRowData();
            $this->db->_fixResultArrayValues($row, $mode);
            $this->_fixResultArrayValues_Test_Rtrim($row);
            $this->_fixResultArrayValues_Test_FixAssocFieldNames($row);
    
            $mode = MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES;
            $row = $this->_fetchRowData();
            $this->db->_fixResultArrayValues($row, $mode);
            $this->_fixResultArrayValues_Test_EmptyToNull($row);
            $this->_fixResultArrayValues_Test_Rtrim($row);
            $this->_fixResultArrayValues_Test_FixAssocFieldNames($row);
        }
    
        /**
         * Tests that the MDB2::transaction() method returns expected values
         * when starting or rolling back a transaction, and for testing if
         * the connection is in a transaction.
         * @dataProvider provider
         */
        public function test_transaction($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->db->supports('transactions'))
            {
                $this->assertTrue($this->db->beginTransaction(), 'transaction');
                $this->assertTrue($this->db->in_transaction, 'transaction');
                $this->assertTrue($this->db->rollback(), 'transaction');
                $this->assertFalse($this->db->in_transaction, 'transaction');
    
                $this->assertTrue($this->db->beginTransaction(), 'transaction');
                $this->assertTrue($this->db->in_transaction, 'transaction');
                $this->assertTrue($this->db->commit(), 'transaction');
                $this->assertFalse($this->db->in_transaction, 'transaction');
            }
        }
    
        // Nested transactions are not yet tested, due to a MySQL 5 problem with
        // savepoints causing netsted transactions to fail.
        //
        // See http://bugs.mysql.com/bug.php?id=26288
    
        /**
         * Tests that the MDB2::setDatabase() and MDB2::getDatabase() methods
         * correctly set and get the database name.
         * @dataProvider provider
         */
        public function test_setGetDatabase($ci) {
            $this->manualSetUp($ci);
    
            $old_name = $this->db->database_name;
            $this->assertEquals($old_name, $this->db->setDatabase('test_database'), 'setDatabase');
            $this->assertEquals('test_database', $this->db->database_name, 'setDatabase');
            $this->assertEquals('test_database', $this->db->getDatabase(), 'getDatabase');
            $this->db->database_name = $old_name;
        }
    
        /**
         * Tests that the MDB2::setDSN() method correctly sets the DSN.
         * @dataProvider provider
         */
        public function test_setDSN($ci) {
            $this->manualSetUp($ci);
    
            $dsn = "mydbms://myname:mypassword@localhost";
            $result = $this->db->setDSN($dsn);
            $dsn_set = $this->db->dsn;
    
            $this->assertEquals('mydbms', $dsn_set['phptype'],'setDSN');
            $this->assertEquals('mydbms',$dsn_set['dbsyntax'],'setDSN');
            $this->assertEquals('tcp',$dsn_set['protocol'],'setDSN');
            $this->assertEquals('localhost',$dsn_set['hostspec'],'setDSN');
            $this->assertEquals(false,$dsn_set['port'],'setDSN');
            $this->assertEquals(false,$dsn_set['socket'],'setDSN');
            $this->assertEquals('myname',$dsn_set['username'],'setDSN');
            $this->assertEquals('mypassword',$dsn_set['password'],'setDSN');
            $this->assertEquals(false,$dsn_set['database'],'setDSN');
        }
    
        /**
         * Tests that the MDB2::getDSN() method correctly gets the DSN.
         * @dataProvider provider
         */
        public function test_getDSN($ci) {
            $this->manualSetUp($ci);
    
            $dsn_set = "mydbms://myname:mypassword@localhost";
            $result = $this->db->setDSN($dsn_set);
            $dsn_get = $this->db->getDSN();
            $dsn_rex = "/(([\w]+)\(mydbms\):\/\/myname:mypassword@localhost\/)/";
            //preg_match($dsn_rex, $dsn_get, $matches);
            $this->assertRegExp($dsn_rex, $dsn_get, 'testGetDSN');
            $dsn_rex = "/{$this->dsn['phptype']}[\w\W]+/";
            $this->assertRegExp($dsn_rex, $dsn_get, 'testGetDSN');
    
            $dsn_set = "mydbms://myname:mypassword@localhost";
            $result = $this->db->setDSN($dsn_set);
            $dsn_get = $this->db->getDSN('string', true);
            $dsn_rex = "/(([\w]+)\(mydbms\):\/\/myname:1@localhost\/)/";
            $this->assertRegExp($dsn_rex, $dsn_get, 'testGetDSN');
            $dsn_rex = "/{$this->dsn['phptype']}[\w\W]+/";
            $this->assertRegExp($dsn_rex, $dsn_get, 'testGetDSN');
        }
    
        /**
         * Tests that the 'new_link' DSN option is read correctly
         * @dataProvider provider
         */
        public function test_isNewLinkSet($ci) {
            $this->manualSetUp($ci);
    
            $dsn = array(
                'phptype'  => 'mydbms',
                'host'     => 'localhost',
                'database' => 'dbname',
                'username' => 'myname',
                'password' => 'mypassword',
            );
            $this->db->setDSN($dsn);
            $this->assertFalse($this->db->_isNewLinkSet());
            $dsn['new_link'] = true;
            $this->db->setDSN($dsn);
            $this->assertTrue($this->db->_isNewLinkSet());
            $dsn['new_link'] = false;
            $this->db->setDSN($dsn);
            $this->assertFalse($this->db->_isNewLinkSet());
            $dsn['new_link'] = 'true';
            $this->db->setDSN($dsn);
            $this->assertTrue($this->db->_isNewLinkSet());
            $dsn['new_link'] = 'false';
            $this->db->setDSN($dsn);
            $this->assertFalse($this->db->_isNewLinkSet());
            $dsn['new_link'] = 1;
            $this->db->setDSN($dsn);
            $this->assertTrue($this->db->_isNewLinkSet());
            $dsn['new_link'] = 0;
            $this->db->setDSN($dsn);
            $this->assertFalse($this->db->_isNewLinkSet());
            $dsn['new_link'] = '1';
            $this->db->setDSN($dsn);
            $this->assertTrue($this->db->_isNewLinkSet());
            $dsn['new_link'] = '0';
            $this->db->setDSN($dsn);
            $this->assertFalse($this->db->_isNewLinkSet());
            $dsn['new_link'] = 'True';
            $this->db->setDSN($dsn);
            $this->assertTrue($this->db->_isNewLinkSet());
            $dsn['new_link'] = 'TRUE';
            $this->db->setDSN($dsn);
            $this->assertTrue($this->db->_isNewLinkSet());
            //now test some invalid values...
            $dsn['new_link'] = new StdClass;
            $this->db->setDSN($dsn);
            $this->assertFalse($this->db->_isNewLinkSet());
            $dsn['new_link'] = '';
            $this->db->setDSN($dsn);
            $this->assertFalse($this->db->_isNewLinkSet());
            $dsn['new_link'] = 'blah';
            $this->db->setDSN($dsn);
            $this->assertFalse($this->db->_isNewLinkSet());
        }
    
        /**
         * Tests that the MDB2::setLimit() method correctly sets the limit
         * and offset values.
         * @dataProvider provider
         */
        public function test_setLimit($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->db->supports('limit_queries'))
            {
                $this->db->limit = null;
                $this->db->offset = null;
                $this->db->setLimit(100, 50);
                $this->assertEquals(100, $this->db->limit , 'setLimit');
                $this->assertEquals( 50, $this->db->offset, 'setLimit');
            }
        }
    
        /**
         * Tests that the MDB2::supports() method correctly finds keys
         * in the "supports" array.
         * @dataProvider provider
         */
        public function test_supports($ci) {
            $this->manualSetUp($ci);
    
            $this->db->supported['testkey'] = true;
            $this->assertTrue($this->db->supports('testkey'), 'supports');
            unset($this->db->supported['testkey']);
        }
    
        /**
         * Tests that the MDB2::getSequenceName() method correctly gets
         * sequence names.
         * @dataProvider provider
         */
        public function test_getSequenceName($ci) {
            $this->manualSetUp($ci);
    
            $tmp = $this->db->options['seqname_format'];
            $this->db->options['seqname_format'] = '%s_seq';
            $this->assertEquals('test_seq', strtolower($this->db->getSequenceName('test')), 'getSequenceName');
            $this->db->options['seqname_format'] = $tmp;
        }
    
        /**
         * Tests that the MDB2::getIndexName() method correctly gets index names.
         * @dataProvider provider
         */
        public function test_getIndexName($ci) {
            $this->manualSetUp($ci);
    
            $tmp = $this->db->options['idxname_format'];
            $this->db->options['idxname_format'] = 'idx_%s';
            $this->assertEquals('idx_test', $this->db->getIndexName('test'), 'getIndexName');
            $this->db->options['idxname_format'] = $tmp;
        }
    
        /**
         * Tests that the MDB2::disconnect() method correctly disconnects.
         * @dataProvider provider
         */
        public function test_disconnect($ci) {
            $this->manualSetUp($ci);
    
            $this->db->connect();
            $this->assertTrue($this->db->disconnect(), 'disconnect');
            $this->assertEquals(0, $this->db->connection, 'disconnect');
            $this->assertEquals(array(), $this->db->connected_dsn, 'disconnect');
            $this->assertEquals('', $this->db->connected_database_name, 'disconnect');
            $this->assertNull($this->db->opened_persistent, 'disconnect');
            $this->assertEquals('', $this->db->connected_server_info, 'disconnect');
            $this->assertNull($this->db->in_transaction, 'disconnect');
            $this->assertNull($this->db->nested_transaction_counter, 'disconnect');
        }
    
        /**
         * Test that the MDB2::_skipDelimitedStrings() method correctly recognizes
         * parameter placeholders from quoted strings
         * @dataProvider provider
         */
        public function test_skipDelimitedStrings($ci) {
            $this->manualSetUp($ci);
    
            $query = "UPDATE tbl SET fld='' WHERE fld2=:param AND fld3=':fakeparam' AND fld3=:param2";
            $this->assertEquals(0, $this->db->_skipDelimitedStrings($query, 0, 0));
            $this->assertEquals(18, $this->db->_skipDelimitedStrings($query, 18, 19));
            $this->assertEquals(20, $this->db->_skipDelimitedStrings($query, 20, 20));
            $this->assertEquals(21, $this->db->_skipDelimitedStrings($query, 19, 21));
            $this->assertEquals(30, $this->db->_skipDelimitedStrings($query, 30, 33));
            $this->assertEquals(30, $this->db->_skipDelimitedStrings($query, 30, 34));
            $this->assertEquals(33, $this->db->_skipDelimitedStrings($query, 33, 33));
            $this->assertEquals(50, $this->db->_skipDelimitedStrings($query, 50, 50));
            $this->assertEquals(61, $this->db->_skipDelimitedStrings($query, 49, 51));
            $this->assertEquals(52, $this->db->_skipDelimitedStrings($query, 52, 52));
            $this->assertEquals(70, $this->db->_skipDelimitedStrings($query, 70, 72));
            $this->assertEquals(71, $this->db->_skipDelimitedStrings($query, 71, 72));
            $this->assertEquals(72, $this->db->_skipDelimitedStrings($query, 72, 72));
    
            //be careful about SQL comments that are not comments (because within quotes)
            $query = "UPDATE tbl SET fld='--some text' WHERE col2=?";
            $this->assertEquals(0, $this->db->_skipDelimitedStrings($query, 0, 0));
            $this->assertEquals(18, $this->db->_skipDelimitedStrings($query, 18, 19));
            $this->assertEquals(20, $this->db->_skipDelimitedStrings($query, 20, 20));
            $this->assertEquals(32, $this->db->_skipDelimitedStrings($query, 19, 21));
        }
    
        /**
         * Double check that errors are a MDB2_Error
         * @see http://pear.php.net/bugs/bug.php?id=19677
         * @dataProvider provider
         */
        public function testMDB2_Error($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->query("GOTTA FAIL");
            if (!MDB2::isError($result)) {
                $this->fail("What!?  That should have been a MDB2 error.");
            }
        }
    
        /**
         * Double check that errors are a PEAR_Error
         * @see http://pear.php.net/bugs/bug.php?id=19677
         * @depends testMDB2_Error
         * @dataProvider provider
         */
        public function testPEAR_Error($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->query("GOTTA FAIL");
            if (!@PEAR::isError($result)) {
                $this->fail("What!?  That should have been a PEAR error.");
            }
        }
    
        /**
         * Call to undefined function: MDB2_Driver_Common::isError()
         * @see http://pear.php.net/bugs/bug.php?id=19677
         * @depends testMDB2_Error
         * @dataProvider provider
         * @expectedException PHPUnit_Framework_Error
         * @expectedExceptionMessage isError() is deprecated, use MDB2::isError()
         */
        public function testDriverIsError_object($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->query("GOTTA FAIL");
            // Problem raised in bug 19677.
            if (!$this->db->isError($result)) {
                $this->fail("What!?  That should have been an OOP error.");
            }
        }
    
        /**
         * Call to undefined function: MDB2_Driver_Common::isError()
         * @see http://pear.php.net/bugs/bug.php?id=19677
         * @depends testMDB2_Error
         * @dataProvider provider
         * @expectedException PHPUnit_Framework_Error
         * @expectedExceptionMessage isError() is deprecated, use MDB2::isError()
         */
        public function testDriverIsError_static($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->query("GOTTA FAIL");
            // Cover this scenario too.
            $class = get_class($this->db);
            if (!$class::isError($result)) {
                $this->fail("What!?  That should have been a static error.");
            }
        }
    
        /**
         * Parameter missing for MDB2_Driver_Common::isError()
         * @see http://pear.php.net/bugs/bug.php?id=19677
         * @depends testMDB2_Error
         * @dataProvider provider
         * @expectedException PHPUnit_Framework_Error
         * @expectedExceptionMessage isError() is deprecated, use MDB2::isError()
         * @expectedExceptionMessage Missing argument 1
         */
        public function testDriverIsError_object_param_missing($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->db->isError()) {
                $this->fail("What!?  That should have been an OOP error.");
            }
        }
    
        /**
         * Parameter missing for MDB2_Driver_Common::isError()
         * @see http://pear.php.net/bugs/bug.php?id=19677
         * @depends testMDB2_Error
         * @dataProvider provider
         * @expectedException PHPUnit_Framework_Error
         * @expectedExceptionMessage isError() is deprecated, use MDB2::isError()
         * @expectedExceptionMessage Missing argument 1
         */
        public function testDriverIsError_static_param_missing($ci) {
            $this->manualSetUp($ci);
    
            $class = get_class($this->db);
            if (!$class::isError()) {
                $this->fail("What!?  That should have been a static error.");
            }
        }
    
        /**
         * Call to undefined function: MDB2_Driver_common::unknownfunction
         * @see http://pear.php.net/bugs/bug.php?id=19677
         * @depends testMDB2_Error
         * @dataProvider provider
         * @expectedException PHPUnit_Framework_Error
         * @expectedExceptionMessage Call to undefined function
         */
        public function testDriverIsError_object_unknown($ci) {
            $this->manualSetUp($ci);
    
            $result = $this->db->unkownfunction();
        }
    
        /**
         * Call to undefined function: MDB2_Driver_common::unknownfunction
         * @see http://pear.php.net/bugs/bug.php?id=19677
         * @depends testMDB2_Error
         * @dataProvider provider
         * @expectedException PHPUnit_Framework_Error
         * @expectedExceptionMessage Call to undefined function
         */
        public function testDriverIsError_static_unknown($ci) {
            $this->manualSetUp($ci);
    
            $class = get_class($this->db);
            $class::unknownfunction();
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/ManagerTest.php000066400000000000000000001214411213666021100225570ustar00rootroot00000000000000                                |
    // |          Lorenzo Alberton            |
    // |          Daniel Convissor                           |
    // +----------------------------------------------------------------------+
    //
    // $Id: ManagerTest.php 327313 2012-08-27 15:16:42Z danielc $
    
    require_once dirname(__DIR__) . '/autoload.inc';
    
    class Standard_ManagerTest extends Standard_Abstract {
        //test table name (it is dynamically created/dropped)
        public $table = 'newtable';
    
        /**
         * The non-standard helper
         * @var Nonstandard_Base
         */
        protected $nonstd;
    
    
        /**
         * Can not use setUp() because we are using a dataProvider to get multiple
         * MDB2 objects per test.
         *
         * @param array $ci  an associative array with two elements.  The "dsn"
         *                   element must contain an array of DSN information.
         *                   The "options" element must be an array of connection
         *                   options.
         */
        protected function manualSetUp($ci) {
            parent::manualSetUp($ci);
    
            $this->nonstd = Nonstandard_Base::factory($this->db, $this);
    
            $this->db->loadModule('Manager', null, true);
            $this->fields = array(
                'id' => array(
                    'type'     => 'integer',
                    'unsigned' => true,
                    'notnull'  => true,
                    'default'  => 0,
                ),
                'somename' => array(
                    'type'     => 'text',
                    'length'   => 12,
                ),
                'somedescription'  => array(
                    'type'     => 'text',
                    'length'   => 12,
                ),
                'sex' => array(
                    'type'     => 'text',
                    'length'   => 1,
                    'default'  => 'M',
                ),
            );
            $options = array();
            if ('mysql' == substr($this->db->phptype, 0, 5)) {
                $options['type'] = 'innodb';
            }
            if (!$this->tableExists($this->table)) {
                $result = $this->db->manager->createTable($this->table, $this->fields, $options);
                $this->checkResultForErrors($result, 'createTable');
            }
        }
    
        public function tearDown() {
            if (!$this->db || MDB2::isError($this->db)) {
                return;
            }
            if ($this->tableExists($this->table)) {
                $this->db->manager->dropTable($this->table);
            }
            parent::tearDown();
        }
    
        /**
         * @covers MDB2_Driver_Manager_Common::createTable()
         * @covers MDB2_Driver_Manager_Common::listTables()
         * @covers MDB2_Driver_Manager_Common::dropTable()
         * @dataProvider provider
         */
        public function testTableActions($ci) {
            $this->manualSetUp($ci);
    
            // Make sure it doesn't exist before trying to create it.
            if ($this->methodExists($this->db->manager, 'dropTable')) {
                $this->db->manager->dropTable($this->table);
            } else {
                $this->db->exec("DROP TABLE $this->table");
            }
    
            $action = 'createTable';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
            $result = $this->db->manager->createTable($this->table, $this->fields);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'listTables';
            if ($this->methodExists($this->db->manager, $action)) {
                $result = $this->db->manager->listTables();
                $this->checkResultForErrors($result, $action);
                $this->assertContains($this->table, $result,
                        "Result of $action() does not contain expected value");
            }
    
            $action = 'dropTable';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->db->exec("DROP TABLE $this->table");
                $this->markTestSkipped("Driver lacks $action() method");
            }
            $result = $this->db->manager->dropTable($this->table);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            // Check that it's actually gone.
            if ($this->tableExists($this->table)) {
                $this->fail("dropTable() passed but the table still exists");
            }
        }
    
        /**
         * Create a sample table, test the new fields, and drop it.
         * @dataProvider provider
         */
        public function testCreateAutoIncrementTable($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->manager, 'createTable')) {
                $this->markTestSkipped("Driver lacks createTable() method");
            }
            if ($this->tableExists($this->table)) {
                $this->db->manager->dropTable($this->table);
            }
            $seq_name = $this->table;
            if ('ibase' == $this->db->phptype) {
                $seq_name .= '_id';
            }
            //remove existing PK sequence
            $sequences = $this->db->manager->listSequences();
            if (in_array($seq_name, $sequences)) {
                $this->db->manager->dropSequence($seq_name);
            }
    
            $action = 'createTable';
            $fields = $this->fields;
            $fields['id']['autoincrement'] = true;
            $result = $this->db->manager->createTable($this->table, $fields);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $query = 'INSERT INTO '.$this->db->quoteIdentifier($this->table, true);
            $query.= ' (somename, somedescription)';
            $query.= ' VALUES (:somename, :somedescription)';
            $stmt = $this->db->prepare($query, array('text', 'text'), MDB2_PREPARE_MANIP);
            $this->checkResultForErrors($stmt, 'prepare');
    
            $values = array(
                'somename' => 'foo',
                'somedescription' => 'bar',
            );
            $rows = 5;
            for ($i =0; $i < $rows; ++$i) {
                $result = $stmt->execute($values);
                $this->checkResultForErrors($result, 'execute');
            }
            $stmt->free();
    
            $query = 'SELECT id FROM '.$this->table;
            $data = $this->db->queryCol($query, 'integer');
            $this->checkResultForErrors($data, 'queryCol');
            for ($i=0; $i<$rows; ++$i) {
                if (!isset($data[$i])) {
                    $this->fail('Error in data returned by select');
                }
                if ($data[$i] !== ($i+1)) {
                    $this->fail('Error executing autoincrementing insert');
                }
            }
        }
    
        /**
         * @dataProvider provider
         */
        public function testListTableFields($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->manager, 'listTableFields')) {
                $this->markTestSkipped("Driver lacks listTableFields() method");
            }
            $this->assertEquals(
                array_keys($this->fields),
                $this->db->manager->listTableFields($this->table),
                'Error creating table: incorrect fields'
            );
        }
    
        /**
         * @covers MDB2_Driver_Manager_Common::createIndex()
         * @covers MDB2_Driver_Manager_Common::listTableIndexes()
         * @covers MDB2_Driver_Manager_Common::dropIndex()
         * @dataProvider provider
         */
        public function testIndexActions($ci) {
            $this->manualSetUp($ci);
    
            $index = array(
                'fields' => array(
                    'somename' => array(
                        'sorting' => 'ascending',
                    ),
                ),
            );
            $name = 'simpleindex';
    
            $action = 'createIndex';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
            $result = $this->db->manager->createIndex($this->table, $name, $index);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'listTableIndexes';
            if ($this->methodExists($this->db->manager, $action)) {
                $result = $this->db->manager->listTableIndexes($this->table);
                $this->checkResultForErrors($result, $action);
                $this->assertContains($name, $result,
                        "Result of $action() does not contain expected value");
            }
    
            $action = 'dropIndex';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
            $result = $this->db->manager->dropIndex($this->table, $name);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            // Check that it's actually gone.
            $action = 'listTableIndexes';
            if ($this->methodExists($this->db->manager, $action)) {
                $result = $this->db->manager->listTableIndexes($this->table);
                $this->checkResultForErrors($result, $action);
                $this->assertNotContains($name, $result,
                        "dropIndex() passed but the index is still there");
            }
        }
    
        /**
         * @dataProvider provider
         */
        public function testCreatePrimaryKey($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->manager, 'createConstraint')) {
                $this->markTestSkipped("Driver lacks createConstraint() method");
            }
            $constraint = array(
                'fields' => array(
                    'id' => array(
                        'sorting' => 'ascending',
                    ),
                ),
                'primary' => true,
            );
            $name = 'pkindex';
    
            $action = 'createConstraint';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
            $result = $this->db->manager->createConstraint($this->table, $name, $constraint);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
        }
    
        /**
         * @covers MDB2_Driver_Manager_Common::createConstraint()
         * @covers MDB2_Driver_Manager_Common::listTableConstraints()
         * @covers MDB2_Driver_Manager_Common::dropConstraint()
         * @dataProvider provider
         */
        public function testConstraintActions($ci) {
            $this->manualSetUp($ci);
    
            $constraint = array(
                'fields' => array(
                    'id' => array(
                        'sorting' => 'ascending',
                    ),
                ),
                'unique' => true,
            );
            $name = 'uniqueindex';
    
            $action = 'createConstraint';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
            // Make sure it doesn't exist before trying to create it.
            $this->db->manager->dropConstraint($this->table, $name);
            $result = $this->db->manager->createConstraint($this->table, $name, $constraint);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'listTableConstraints';
            $result = $this->db->manager->listTableConstraints($this->table);
            $this->checkResultForErrors($result, $action);
            $this->assertContains($name, $result,
                    "Result of $action() does not contain expected value");
    
            $action = 'dropConstraint';
            $result = $this->db->manager->dropConstraint($this->table, $name);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            // Check that it's actually gone.
            $action = 'listTableConstraints';
            $result = $this->db->manager->listTableConstraints($this->table);
            $this->checkResultForErrors($result, $action);
            $this->assertNotContains($name, $result,
                    "dropConstraint() passed but the constraint is still there");
        }
    
        /**
         * MYSQL NOTE:  If this test fails with native code 1005
         * "Can't create table './peartest/#sql-540_2c871.frm' (errno: 150)"
         * that means your server's default storage engine is MyISAM.
         * Edit my.cnf to have "default-storage-engine = InnoDB"
         *
         * @dataProvider provider
         */
        public function testCreateForeignKeyConstraint($ci) {
            $this->manualSetUp($ci);
    
            $constraint = array(
                'fields' => array(
                    'id' => array(
                        'sorting' => 'ascending',
                    ),
                ),
                'foreign' => true,
                'references' => array(
                    'table' => $this->table_users,
                    'fields' => array(
                        'user_id' => array(
                            'position' => 1,
                        ),
                    ),
                ),
                'initiallydeferred' => false,
                'deferrable' => false,
                'match' => 'SIMPLE',
                'onupdate' => 'CASCADE',
                'ondelete' => 'CASCADE',
            );
    
            $name = 'fkconstraint';
    
            $action = 'createConstraint';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
            // Make sure it doesn't exist before trying to create it.
            $this->db->manager->dropConstraint($this->table, $name);
            $result = $this->db->manager->createConstraint($this->table, $name, $constraint);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'listTableConstraints';
            $result = $this->db->manager->listTableConstraints($this->table);
            $this->checkResultForErrors($result, $action);
            $name_idx = $this->db->getIndexName($name);
            $this->checkResultForErrors($name_idx, 'getIndexName');
            $this->assertTrue(in_array($name_idx, $result)
                    || in_array($name, $result),
                    "Result of $action() does not contain expected value");
    
    
            //now check that it is enforced...
    
            //insert a row in the primary table
            $result = $this->db->exec('INSERT INTO ' . $this->table_users . ' (user_id) VALUES (1)');
            $this->checkResultForErrors($result, 'exec');
    
            //insert a row in the FK table with an id that references
            //the newly inserted row on the primary table: should not fail
            $query = 'INSERT INTO '.$this->db->quoteIdentifier($this->table, true)
                    .' ('.$this->db->quoteIdentifier('id', true).') VALUES (1)';
            $result = $this->db->exec($query);
            $this->checkResultForErrors($result, 'exec');
    
            //try to insert a row into the FK table with an id that does not
            //exist in the primary table: should fail
            $query = 'INSERT INTO '.$this->db->quoteIdentifier($this->table, true)
                    .' ('.$this->db->quoteIdentifier('id', true).') VALUES (123456)';
            $this->db->pushErrorHandling(PEAR_ERROR_RETURN);
            $this->db->expectError('*');
            $result = $this->db->exec($query);
            $this->db->popExpect();
            $this->db->popErrorHandling();
            $this->assertInstanceOf('MDB2_Error', $result,
                    'Foreign Key constraint was not enforced for INSERT query');
            $this->assertEquals(MDB2_ERROR_CONSTRAINT, $result->getCode(),
                    "Wrong error code. See full output for clues.\n"
                    . $result->getUserInfo());
    
            //try to update the first row of the FK table with an id that does not
            //exist in the primary table: should fail
            $query = 'UPDATE '.$this->db->quoteIdentifier($this->table, true)
                    .' SET '.$this->db->quoteIdentifier('id', true).' = 123456 '
                    .' WHERE '.$this->db->quoteIdentifier('id', true).' = 1';
            $this->db->expectError('*');
            $result = $this->db->exec($query);
            $this->db->popExpect();
            $this->assertInstanceOf('MDB2_Error', $result,
                    'Foreign Key constraint was not enforced for UPDATE query');
            $this->assertEquals(MDB2_ERROR_CONSTRAINT, $result->getCode(),
                    "Wrong error code. See full output for clues.\n"
                    . $result->getUserInfo());
    
            $numrows_query = 'SELECT COUNT(*) FROM '. $this->db->quoteIdentifier($this->table, true);
            $numrows = $this->db->queryOne($numrows_query, 'integer');
            $this->assertEquals(1, $numrows, 'Invalid number of rows in the FK table');
    
            //update the PK value of the primary table: the new value should be
            //propagated to the FK table (ON UPDATE CASCADE)
            $result = $this->db->exec('UPDATE ' . $this->table_users . ' SET user_id = 2');
            $this->checkResultForErrors($result, 'exec');
    
            $numrows = $this->db->queryOne($numrows_query, 'integer');
            $this->assertEquals(1, $numrows, 'Invalid number of rows in the FK table');
    
            $query = 'SELECT id FROM '.$this->db->quoteIdentifier($this->table, true);
            $newvalue = $this->db->queryOne($query, 'integer');
            $this->assertEquals(2, $newvalue, 'The value of the FK field was not updated (CASCADE failed)');
    
            //delete the row of the primary table: the row in the FK table should be
            //deleted automatically (ON DELETE CASCADE)
            $result = $this->db->exec('DELETE FROM ' . $this->table_users);
            $this->checkResultForErrors($result, 'exec');
    
            $numrows = $this->db->queryOne($numrows_query, 'integer');
            $this->assertEquals(0, $numrows, 'Invalid number of rows in the FK table (CASCADE failed)');
    
    
            $action = 'dropConstraint';
            $result = $this->db->manager->dropConstraint($this->table, $name);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
        }
    
        /**
         * @dataProvider provider
         */
        public function testDropPrimaryKey($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->manager, 'dropConstraint')) {
                $this->markTestSkipped("Driver lacks dropConstraint() method");
            }
            $index = array(
                'fields' => array(
                    'id' => array(
                        'sorting' => 'ascending',
                    ),
                ),
                'primary' => true,
            );
            $name = 'pkindex';
    
            $action = 'createConstraint';
            $result = $this->db->manager->createConstraint($this->table, $name, $index);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'dropConstraint';
            $result = $this->db->manager->dropConstraint($this->table, $name, true);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
        }
    
        /**
         * @dataProvider provider
         */
        public function testListDatabases($ci) {
            $this->manualSetUp($ci);
    
            $action = 'listDatabases';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
            $result = $this->db->manager->listDatabases();
            $this->checkResultForErrors($result, $action);
            $this->assertTrue(in_array(strtolower($this->database), $result), 'Error listing databases');
        }
    
        /**
         * @dataProvider provider
         */
        public function testAlterTable($ci) {
            $this->manualSetUp($ci);
    
            $newer = 'newertable';
            if ($this->tableExists($newer)) {
                $this->db->manager->dropTable($newer);
            }
            $changes = array(
                'add' => array(
                    'quota' => array(
                        'type' => 'integer',
                        'unsigned' => 1,
                    ),
                    'note' => array(
                        'type' => 'text',
                        'length' => '20',
                    ),
                ),
                'rename' => array(
                    'sex' => array(
                        'name' => 'gender',
                        'definition' => array(
                            'type' => 'text',
                            'length' => 1,
                            'default' => 'M',
                        ),
                    ),
                ),
                'change' => array(
                    'id' => array(
                        'unsigned' => false,
                        'definition' => array(
                            'type'     => 'integer',
                            'notnull'  => false,
                            'default'  => 0,
                        ),
                    ),
                    'somename' => array(
                        'length' => '20',
                        'definition' => array(
                            'type' => 'text',
                            'length' => 20,
                        ),
                    )
                ),
                'remove' => array(
                    'somedescription' => array(),
                ),
                'name' => $newer,
            );
    
            $action = 'alterTable';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
            $this->db->expectError(MDB2_ERROR_CANNOT_ALTER);
            $result = $this->db->manager->alterTable($this->table, $changes, true);
            $this->db->popExpect();
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $result = $this->db->manager->alterTable($this->table, $changes, false);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
        }
    
        /**
         * @dataProvider provider
         */
        public function testAlterTable2($ci) {
            $this->manualSetUp($ci);
    
            $newer = 'newertable2';
            if ($this->tableExists($newer)) {
                $this->db->manager->dropTable($newer);
            }
            $changes_all = array(
                'add' => array(
                    'quota' => array(
                        'type' => 'integer',
                        'unsigned' => 1,
                    ),
                ),
                'rename' => array(
                    'sex' => array(
                        'name' => 'gender',
                        'definition' => array(
                            'type' => 'text',
                            'length' => 1,
                            'default' => 'M',
                        ),
                    ),
                ),
                'change' => array(
                    'somename' => array(
                        'length' => '20',
                        'definition' => array(
                            'type' => 'text',
                            'length' => 20,
                        ),
                    )
                ),
                'remove' => array(
                    'somedescription' => array(),
                ),
                'name' => $newer,
            );
    
            $action = 'alterTable';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
    
            foreach ($changes_all as $type => $change) {
                $changes = array($type => $change);
                $this->db->expectError(MDB2_ERROR_CANNOT_ALTER);
                $result = $this->db->manager->alterTable($this->table, $changes, true);
                $this->db->popExpect();
                $this->checkResultForErrors($result, $action);
                $this->assertEquals(MDB2_OK, $result,
                        "$action did not return MDB2_OK");
    
                $result = $this->db->manager->alterTable($this->table, $changes, false);
                $this->checkResultForErrors($result, $action);
                $this->assertEquals(MDB2_OK, $result,
                        "$action did not return MDB2_OK");
    
                switch ($type) {
                    case 'add':
                        $altered_table_fields = $this->db->manager->listTableFields($this->table);
                        $this->checkResultForErrors($altered_table_fields, 'listTableFields');
                        foreach ($change as $newfield => $dummy) {
                            $this->assertContains($newfield, $altered_table_fields,
                                    "Field '$newfield' was not added");
                        }
                        break;
                    case 'rename':
                        $altered_table_fields = $this->db->manager->listTableFields($this->table);
                        $this->checkResultForErrors($altered_table_fields, 'listTableFields');
                        foreach ($change as $oldfield => $newfield) {
                            $this->assertNotContains($oldfield, $altered_table_fields,
                                    "Field '$oldfield' was not renamed");
    
                            $this->assertContains($newfield['name'], $altered_table_fields,
                                    "While '$oldfield' is gone, '{$newfield['name']}' is not there");
                        }
                        break;
                    case 'change':
                        break;
                    case 'remove':
                        $altered_table_fields = $this->db->manager->listTableFields($this->table);
                        $this->checkResultForErrors($altered_table_fields, 'listTableFields');
                        foreach ($change as $newfield => $dummy) {
                            $this->assertNotContains($newfield, $altered_table_fields,
                                    "Field '$oldfield' was not removed");
                        }
                        break;
                    case 'name':
                        if ($this->tableExists($newer)) {
                            $this->db->manager->dropTable($newer);
                        } else {
                            $this->fail('Error: table "'.$this->table.'" not renamed');
                        }
                        break;
                }
            }
        }
    
        /**
         * @dataProvider provider
         */
        public function testTruncateTable($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->manager, 'truncateTable')) {
                $this->markTestSkipped("Driver lacks truncateTable() method");
            }
    
            $query = 'INSERT INTO '.$this->table;
            $query.= ' (id, somename, somedescription)';
            $query.= ' VALUES (:id, :somename, :somedescription)';
            $stmt = $this->db->prepare($query, array('integer', 'text', 'text'), MDB2_PREPARE_MANIP);
            $this->checkResultForErrors($stmt, 'prepare');
    
            $rows = 5;
            for ($i=1; $i<=$rows; ++$i) {
                $values = array(
                    'id' => $i,
                    'somename' => 'foo'.$i,
                    'somedescription' => 'bar'.$i,
                );
                $result = $stmt->execute($values);
                $this->checkResultForErrors($result, 'execute');
            }
            $stmt->free();
            $count = $this->db->queryOne('SELECT COUNT(*) FROM '.$this->table, 'integer');
            $this->checkResultForErrors($count, 'queryOne');
            $this->assertEquals($rows, $count, 'Error: invalid number of rows returned');
    
            $action = 'truncateTable';
            $result = $this->db->manager->truncateTable($this->table);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $count = $this->db->queryOne('SELECT COUNT(*) FROM '.$this->table, 'integer');
            $this->checkResultForErrors($count, 'queryOne');
            $this->assertEquals(0, $count, 'Error: invalid number of rows returned');
        }
    
        /**
         * @dataProvider provider
         */
        public function testListTablesNoTable($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->manager, 'listTables')) {
                $this->markTestSkipped("Driver lacks listTables() method");
            }
            $result = $this->db->manager->dropTable($this->table);
            $this->assertFalse($this->tableExists($this->table), 'Error listing tables');
        }
    
        /**
         * @covers MDB2_Driver_Manager_Common::createSequence()
         * @covers MDB2_Driver_Manager_Common::listSequences()
         * @covers MDB2_Driver_Manager_Common::dropSequence()
         * @dataProvider provider
         */
        public function testSequences($ci) {
            $this->manualSetUp($ci);
    
            $name = 'testsequence';
    
            $action = 'createSequence';
            if (!$this->methodExists($this->db->manager, $action)) {
                $this->markTestSkipped("Driver lacks $action() method");
            }
            // Make sure it doesn't exist before trying to create it.
            $this->db->manager->dropSequence($name);
            $result = $this->db->manager->createSequence($name);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'listSequences';
            $result = $this->db->manager->listSequences();
            $this->checkResultForErrors($result, $action);
            $this->assertContains($name, $result,
                    "Result of $action() does not contain expected value");
    
            $action = 'dropSequence';
            $result = $this->db->manager->dropSequence($name);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            // Check that it's actually gone.
            $action = 'listSequences';
            $result = $this->db->manager->listSequences();
            $this->checkResultForErrors($result, $action);
            $this->assertNotContains($name, $result,
                    "dropSequence() passed but the sequence is still there");
        }
    
        /**
         * @covers MDB2_Driver_Manager_Common::listTableTriggers()
         * @dataProvider provider
         */
        public function testListTableTriggers($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->nonstd) {
                $this->markTestSkipped('No Nonstandard Helper for this phptype.');
            }
    
            $name = 'test_newtrigger';
    
            /*
             * Have test suite helper functions setup the environment.
             */
            $this->nonstd->dropTrigger($name, $this->table);
            $result = $this->nonstd->createTrigger($name, $this->table);
            $this->checkResultForErrors($result, 'create trigger helper');
    
    
            /*
             * The actual tests.
             */
            $action = 'listTableTriggers';
            $result = $this->db->manager->listTableTriggers($this->table);
            $this->checkResultForErrors($result, $action);
            $this->assertContains($name, $result,
                    "Result of $action() does not contain expected value");
    
            $action = 'listTableTriggers on non-existant table';
            $result = $this->db->manager->listTableTriggers('fake_table');
            $this->checkResultForErrors($result, $action);
            $this->assertNotContains($name, $result,
                    "$action should not contain this view");
    
    
            /*
             * Have test suite helper functions clean up the environment.
             */
            $result = $this->nonstd->dropTrigger($name, $this->table);
            $this->checkResultForErrors($result, 'drop trigger helper');
        }
    
        /**
         * @covers MDB2_Driver_Manager_Common::listTableViews()
         * @dataProvider provider
         */
        public function testListTableViews($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->nonstd) {
                $this->markTestSkipped('No Nonstandard Helper for this phptype.');
            }
    
            $name = 'test_newview';
    
            /*
             * Have test suite helper functions setup the environment.
             */
            $this->nonstd->dropView($name);
            $result = $this->nonstd->createView($name, $this->table);
            $this->checkResultForErrors($result, 'create view helper');
    
    
            /*
             * The actual tests.
             */
            $action = 'listTableViews';
            $result = $this->db->manager->listTableViews($this->table);
            $this->checkResultForErrors($result, $action);
            $this->assertContains($name, $result,
                    "Result of $action() does not contain expected value");
    
            $action = 'listTableViews on non-existant table';
            $result = $this->db->manager->listTableViews('fake_table');
            $this->checkResultForErrors($result, $action);
            $this->assertNotContains($name, $result,
                    "$action should not contain this view");
    
    
            /*
             * Have test suite helper functions clean up the environment.
             */
            $result = $this->nonstd->dropView($name);
            $this->checkResultForErrors($result, 'drop view helper');
        }
    
        /**
         * Test listUsers()
         * @dataProvider provider
         */
        public function testListUsers($ci) {
            $this->manualSetUp($ci);
    
            $action = 'listUsers';
            $result = $this->db->manager->listUsers();
            $this->checkResultForErrors($result, $action);
            $result = array_map('strtolower', $result);
            $this->assertContains(strtolower($this->db->dsn['username']), $result,
                    "Result of $action() does not contain expected value");
        }
    
        /**
         * @covers MDB2_Driver_Manager_Common::listFunctions()
         * @dataProvider provider
         */
        public function testFunctionActions($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->nonstd) {
                $this->markTestSkipped('No Nonstandard Helper for this phptype.');
            }
    
            $name = 'test_add';
    
            /*
             * Have test suite helper functions setup the environment.
             */
            $this->nonstd->dropFunction($name);
            $this->db->pushErrorHandling(PEAR_ERROR_RETURN);
            $this->db->expectError('*');
            $result = $this->nonstd->createFunction($name);
            $this->db->popExpect();
            $this->db->popErrorHandling();
            $this->checkResultForErrors($result, 'crete function helper');
    
    
            /*
             * The actual tests.
             */
            $action = 'listFunctions';
            $result = $this->db->manager->listFunctions();
            $this->checkResultForErrors($result, $action);
            $this->assertContains($name, $result,
                    "Result of $action() does not contain expected value");
    
    
            /*
             * Have test suite helper functions clean up the environment.
             */
            $result = $this->nonstd->dropFunction($name);
            $this->checkResultForErrors($result, 'drop function helper');
        }
    
        /**
         * @covers MDB2_Driver_Manager_Common::createDatabase()
         * @covers MDB2_Driver_Manager_Common::alterDatabase()
         * @covers MDB2_Driver_Manager_Common::listDatabases()
         * @covers MDB2_Driver_Manager_Common::dropDatabase()
         * @dataProvider provider
         */
        public function testCrudDatabase($ci) {
            $this->manualSetUp($ci);
    
            $name = 'mdb2_test_newdb';
            $rename = $name . '_renamed';
            $unlink = false;
            switch ($this->db->phptype) {
                case 'sqlite':
                    $name = tempnam(sys_get_temp_dir(), $name);
                    $rename = $name . '_renamed';
                    unlink($name);
                    $unlink = true;
                    break;
            }
    
            $options = array(
                'charset' => 'UTF8',
                'collation' => 'utf8_bin',
            );
            $changes = array(
                'name' => $rename,
                'charset' => 'UTF8',
            );
            if ('pgsql' == substr($this->db->phptype, 0, 5)) {
                $options['charset'] = 'WIN1252';
            }
            if ('mssql' == substr($this->db->phptype, 0, 5)) {
                $options['collation'] = 'WIN1252';
                $options['collation'] = 'Latin1_General_BIN';
            }
    
            $action = 'createDatabase';
            $result = $this->db->manager->createDatabase($name, $options);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'listDatabases';
            $result = $this->db->manager->listDatabases();
            $this->checkResultForErrors($result, $action);
            $this->assertContains($name, $result,
                    "Result of $action() does not contain expected value");
    
            $action = 'alterDatabase';
            $result = $this->db->manager->alterDatabase($name, $changes);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'listDatabases';
            $result = $this->db->manager->listDatabases();
            $this->checkResultForErrors($result, $action);
            if (!in_array($rename, $result)) {
                $this->db->manager->dropDatabase($name);
                $this->fail('Error: could not find renamed database');
            }
    
            $action = 'dropDatabase';
            $result = $this->db->manager->dropDatabase($rename);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            // Check that it's actually gone.
            $action = 'listDatabases';
            $result = $this->db->manager->listDatabases();
            $this->checkResultForErrors($result, $action);
            $this->assertNotContains($rename, $result,
                    "dropDatabase() passed but the database is still there");
        }
    
        /**
         * Test vacuum
         * @dataProvider provider
         */
        public function testVacuum($ci) {
            $this->manualSetUp($ci);
    
            $action = 'vacuum table';
            $result = $this->db->manager->vacuum($this->table);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'vacuum and analyze table';
            $options = array(
                'analyze' => true,
                'full'    => true,
                'freeze'  => true,
            );
            $result = $this->db->manager->vacuum($this->table, $options);
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
    
            $action = 'vacuum all tables';
            $result = $this->db->manager->vacuum();
            $this->checkResultForErrors($result, $action);
            $this->assertEquals(MDB2_OK, $result,
                    "$action did not return MDB2_OK");
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/ReverseTest.php000066400000000000000000000774471213666021100226400ustar00rootroot00000000000000            |
    // +----------------------------------------------------------------------+
    //
    // $Id: ReverseTest.php 322076 2012-01-11 15:51:09Z danielc $
    
    require_once dirname(__DIR__) . '/autoload.inc';
    
    class Standard_ReverseTest extends Standard_Abstract
    {
        //test table name (it is dynamically created/dropped)
        public $table       = 'testtable';
        public $fields      = array();
        public $indices     = array();
        public $constraints = array();
    
        public $table2      = 'testtable2';
        public $fields2      = array();
        public $indices2     = array();
        public $constraints2 = array();
    
        /**
         * The non-standard helper
         * @var Nonstandard_Base
         */
        protected $nonstd;
    
    
        /**
         * Can not use setUp() because we are using a dataProvider to get multiple
         * MDB2 objects per test.
         *
         * @param array $ci  an associative array with two elements.  The "dsn"
         *                   element must contain an array of DSN information.
         *                   The "options" element must be an array of connection
         *                   options.
         */
        protected function manualSetUp($ci) {
            parent::manualSetUp($ci);
    
            $this->nonstd = Nonstandard_Base::factory($this->db, $this);
    
            $this->db->loadModule('Reverse', null, true);
            $this->db->loadModule('Manager', null, true);
    
            //Table structure
            $this->fields = array(
                'id' => array(  //PK
                    'type'     => 'integer',
                    'unsigned' => 1,
                    'notnull'  => 1,
                    'default'  => 0,
                    'length'  => 4,
                ),
                'id2' => array( //UNIQUE_MULTIFIELD(1/2)
                    'type'     => 'integer',
                    'unsigned' => 1,
                    'notnull'  => 1,
                    'default'  => 0,
                ),
                'id3' => array( //UNIQUE_MULTIFIELD(2/2)
                    'type'     => 'integer',
                    'unsigned' => 1,
                    'notnull'  => 1,
                    'default'  => 0,
                ),
                'id4' => array( //UNIQUE
                    'type'     => 'integer',
                    'unsigned' => 1,
                    'notnull'  => 1,
                    'default'  => 0,
                ),
                'somename' => array( //NORMAL INDEX
                    'type'   => 'text',
                    'length' => 12,
                ),
                'somedescription' => array( //INDEX_MULTIFIELD(1/2)
                    'type'   => 'text',
                    'length' => 12,
                ),
                'sex' => array( //INDEX_MULTIFIELD(2/2)
                    'type' => 'text',
                    'length' => 1,
                    'default' => 'M',
                ),
            );
    
            $options = array();
            if ('mysql' == substr($this->db->phptype, 0, 5)) {
                $options['type'] = 'innodb';
            }
    
            if (!$this->tableExists($this->table)) {
                $this->db->manager->createTable($this->table, $this->fields, $options);
            }
    
            //Table2 structure
            $this->fields2 = array(
                'ext_id' => array(  //SINGLE_FK
                    'type'     => 'integer',
                    'unsigned' => 1,
                    'notnull'  => 1,
                    'default'  => 0,
                ),
                'ext_id2' => array( //MULTI_FK(1/2)
                    'type'     => 'integer',
                    'unsigned' => 1,
                    'notnull'  => 1,
                    'default'  => 0,
                ),
                'ext_id3' => array( //MULTI_FK(2/2)
                    'type'     => 'integer',
                    'unsigned' => 1,
                    'notnull'  => 1,
                    'default'  => 0,
                ),
            );
    
            if (!$this->tableExists($this->table2)) {
                $this->db->manager->createTable($this->table2, $this->fields2, $options);
            }
        }
    
        public function tearDown() {
            if (!$this->db || MDB2::isError($this->db)) {
                return;
            }
            if ($this->tableExists($this->table2)) {
                $this->db->manager->dropTable($this->table2);
            }
            if ($this->tableExists($this->table)) {
                $this->db->manager->dropTable($this->table);
            }
            parent::tearDown();
        }
    
        public function setUpIndices()
        {
            //Indices definition
            $this->indices = array(
                'sometestindex' => array(
                    'fields' => array(
                        'somename' => array(
                            'sorting' => 'ascending',
                        ),
                    ),
                    'unique' => false,
                ),
                'multipletestindex' => array(
                    'fields' => array(
                        'somedescription' => array(
                            'sorting' => 'ascending',
                        ),
                        'sex' => array(
                            'sorting' => 'ascending',
                        ),
                    ),
                ),
            );
            foreach ($this->indices as $index_name => $index) {
                $result = $this->db->manager->createIndex($this->table, $index_name, $index);
                $this->assertFalse(MDB2::isError($result), 'Error creating index: '.$index_name);
                if (MDB2::isError($result)) {
                    break;
                }
            }
            return MDB2::isError($result);
        }
    
        public function setUpConstraints()
        {
            //Constraints definition
            $this->constraints = array(
                'pkfield' => array(
                    'fields' => array(
                        'id' => array(
                            'sorting' => 'ascending',
                        ),
                    ),
                    'primary' => true,
                ),
                'multipleunique' => array(
                    'fields' => array(
                        'id2' => array(
                            'sorting' => 'ascending',
                        ),
                        'id3' => array(
                            'sorting' => 'ascending',
                        ),
                    ),
                    'unique' => true,
                ),
                'singleunique' => array(
                    'fields' => array(
                        'id4' => array(
                            'sorting' => 'ascending',
                        ),
                    ),
                    'unique' => true,
                ),
            );
            $failed1 = false;
            foreach ($this->constraints as $constraint_name => $constraint) {
                //$this->db->manager->dropConstraint($this->table, $constraint_name);
                $result = $this->db->manager->createConstraint($this->table, $constraint_name, $constraint);
                //$this->assertFalse(MDB2::isError($result), 'Error creating constraint: '.$constraint_name);
                if (MDB2::isError($result)) {
                    $this->fail('Error creating constraint "'.$constraint_name.'": '.$result->getUserInfo(). ' :: '.$result->getUserInfo());
                    $failed1 = true;
                    break;
                }
            }
    
            $this->fk_constraint1_name = $this->table2.'_fk_'.$this->table.'_id';
            //$this->fk_constraint2_name = $this->table2.'_fk_'.$this->table.'_id2_id3';
            $this->fk_constraint2_name = $this->table2.'_fk_'.$this->table.'_2f';
            $this->constraints2 = array(
                $this->fk_constraint1_name => array(
                    'primary' => false,
                    'unique'  => false,
                    'foreign' => true,
                    'check'   => false,
                    'fields' => array(
                        'ext_id' => array(
                            'position' => 1,
                            'sorting' => 'ascending',
                        ),
                    ),
                    'references' => array(
                        'table'  => $this->table,
                        'fields' => array(
                            'id' => array(
                                'position' => 1,
                            ),
                        ),
                    ),
                    'onupdate' => 'CASCADE',
                    'ondelete' => 'CASCADE',
                    'match'    => 'FULL',
                    'deferrable'        => false,
                    'initiallydeferred' => false,
                ),
                $this->fk_constraint2_name => array(
                    'primary' => false,
                    'unique'  => false,
                    'foreign' => true,
                    'check'   => false,
                    'fields' => array(
                        'ext_id2' => array(
                            'position' => 1,
                            'sorting'  => 'ascending',
                        ),
                        'ext_id3' => array(
                            'position' => 2,
                            'sorting'  => 'ascending',
                        ),
                    ),
                    'references' => array(
                        'table'  => $this->table,
                        'fields' => array(
                            'id2' => array(
                                'position' => 1,
                            ),
                            'id3' => array(
                                'position' => 2,
                            ),
                        ),
                    ),
                    'onupdate' => 'NO ACTION',
                    'ondelete' => 'NO ACTION',
                    'match'    => 'FULL',
                    'deferrable'        => false,
                    'initiallydeferred' => false,
                ),
            );
            $failed2 = false;
            foreach ($this->constraints2 as $constraint_name => $constraint) {
                //$this->db->manager->dropConstraint($this->table, $constraint_name);
                $result = $this->db->manager->createConstraint($this->table2, $constraint_name, $constraint);
                //$this->assertFalse(MDB2::isError($result), 'Error creating constraint: '.$constraint_name);
                if (MDB2::isError($result)) {
                    $this->fail('Error creating constraint "'.$constraint_name.'": '.$result->getUserInfo(). ' :: '.$result->getUserInfo());
                    $failed2 = true;
                    break;
                }
            }
    
            return !($failed1 || $failed2);
        }
    
        /**
         * Test tableInfo('table_name')
         * @dataProvider provider
         */
        public function testTableInfo($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->reverse, 'tableInfo')) {
                $this->markTestSkipped('tableInfo not supported.');
            }
    
            $table_info = $this->db->reverse->tableInfo($this->table);
            if (MDB2::isError($table_info)) {
                $this->fail('Error getting tableInfo(): '.$table_info->getUserInfo());
            } else {
                $this->assertEquals(count($this->fields), count($table_info), 'The number of fields retrieved is different from the expected one');
                foreach ($table_info as $field_info) {
                    $this->assertEquals($this->table, $field_info['table'], 'the table name is not correct');
                    if (!array_key_exists(strtolower($field_info['name']), $this->fields)) {
                        $this->fail('Field names do not match ('.$field_info['name'].' is unknown)');
                    }
                    //expand test, for instance adding a check on types...
                }
            }
    
            if (!$this->supported('result_introspection')) {
                $this->markTestSkipped('Introspection not supported.');
            }
    
            $result = $this->db->query('SELECT * FROM '.$this->table);
            $table_info = $this->db->reverse->tableInfo($result);
            if (MDB2::isError($table_info)) {
                $this->fail('Error getting tableInfo(): '.$table_info->getUserInfo());
            } else {
                $this->assertEquals(count($this->fields), count($table_info), 'The number of fields retrieved is different from the expected one');
                foreach ($table_info as $field_info) {
                    //not all the drivers are capable of returning the table name,
                    //and may return an empty value
                    if (!empty($field_info['table'])) {
                        $this->assertEquals($this->table, $field_info['table'], 'the table name is not correct');
                    }
                    if (!array_key_exists(strtolower($field_info['name']), $this->fields)) {
                        $this->fail('Field names do not match ('.$field_info['name'].' is unknown)');
                    }
                    //expand test, for instance adding a check on types...
                }
            }
            $result->free();
        }
    
        /**
         * Test getTableFieldDefinition($table, $field_name)
         * @dataProvider provider
         */
        public function testGetTableFieldDefinition($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->reverse, 'getTableFieldDefinition')) {
                $this->markTestSkipped('Driver lacks getTableFieldDefinition.');
            }
    
            //test integer not null
            $field_info = $this->db->reverse->getTableFieldDefinition($this->table, 'id');
            if (MDB2::isError($field_info)) {
                $this->fail('Error in getTableFieldDefinition(): '.$field_info->getUserInfo());
            } else {
                $field_info = array_shift($field_info);
                $this->assertEquals('integer', $field_info['type'], 'The field type is different from the expected one');
                $expected_length = ($this->db->phptype == 'oci8') ? 10 : 4;
                $this->assertEquals($expected_length, $field_info['length'], 'The field length is different from the expected one');
                $this->assertTrue($field_info['notnull'], 'The field can be null unlike it was expected');
                $this->assertEquals('0', $field_info['default'], 'The field default value is different from the expected one');
            }
    
            //test blob
            $field_info = $this->db->reverse->getTableFieldDefinition($this->table_files, 'picture');
            if (MDB2::isError($field_info)) {
                $this->fail('Error in getTableFieldDefinition(): '.$field_info->getUserInfo());
            } else {
                $field_info = array_shift($field_info);
                $this->assertEquals($field_info['type'], 'blob', 'The field type is different from the expected one');
                $this->assertFalse($field_info['notnull'], 'The field cannot be null unlike it was expected');
            }
    
            //test varchar(100) not null
            $field_info = $this->db->reverse->getTableFieldDefinition($this->table_users, 'user_name');
            if (MDB2::isError($field_info)) {
                $this->fail('Error in getTableFieldDefinition(): '.$field_info->getUserInfo());
            } else {
                $field_info = array_shift($field_info);
                $this->assertEquals('text', $field_info['type'], 'The field type is different from the expected one');
                $this->assertEquals(12, $field_info['length'], 'The field length is different from the expected one');
                $this->assertFalse($field_info['notnull'], 'The field can be null unlike it was expected');
                $this->assertNull($field_info['default'], 'The field default value is different from the expected one');
                $this->assertFalse($field_info['fixed'], 'The field fixed value is different from the expected one');
            }
    
            //test decimal
            $field_info = $this->db->reverse->getTableFieldDefinition($this->table_users, 'quota');
            if (MDB2::isError($field_info)) {
                $this->fail('Error in getTableFieldDefinition(): '.$field_info->getUserInfo());
            } else {
                $field_info = array_shift($field_info);
                $this->assertEquals('decimal', $field_info['type'], 'The field type is different from the expected one');
                $expected_length = ($this->db->phptype == 'oci8') ? '22,2' : '18,2';
                $this->assertEquals($expected_length, $field_info['length'], 'The field length is different from the expected one');
            }
    
            $field_info = $this->db->reverse->getTableFieldDefinition($this->table_users, 'user_name');
            if (MDB2::isError($field_info)) {
                $this->fail('Error in getTableFieldDefinition(): '.$field_info->getUserInfo());
            } else {
                $field_info = array_shift($field_info);
                $this->assertEquals('text', $field_info['type'], 'The field type is different from the expected one');
                $this->assertEquals(12, $field_info['length'], 'The field length is different from the expected one');
                $this->assertFalse($field_info['notnull'], 'The field can be null unlike it was expected');
                $this->assertNull($field_info['default'], 'The field default value is different from the expected one');
                $this->assertFalse($field_info['fixed'], 'The field fixed value is different from the expected one');
            }
        }
    
        /**
         * Test getTableIndexDefinition($table, $index_name)
         * @dataProvider provider
         */
        public function testGetTableIndexDefinition($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->reverse, 'getTableIndexDefinition')) {
                $this->markTestSkipped('Driver lacks getTableIndexDefinition.');
            }
    
            $this->setUpIndices();
    
            //test index names
            foreach ($this->indices as $index_name => $index) {
                $index_info = $this->db->reverse->getTableIndexDefinition($this->table, $index_name);
                if (MDB2::isError($index_info)) {
                    $this->fail('Error getting table index definition');
                } else {
                    $field_names = array_keys($index['fields']);
                    $this->assertEquals($field_names, array_keys($index_info['fields']), 'Error listing index fields');
                }
            }
    
            //test INDEX
            $index_name = 'sometestindex';
            $index_info = $this->db->reverse->getTableIndexDefinition($this->table, $index_name);
            if (MDB2::isError($index_info)) {
                $this->fail('Error in getTableIndexDefinition(): '.$index_info->getUserInfo());
            } else {
                $this->assertEquals(1, count($index_info['fields']), 'The INDEX is not on one field unlike it was expected');
                $expected_fields = array_keys($this->indices[$index_name]['fields']);
                $actual_fields = array_keys($index_info['fields']);
                $this->assertEquals($expected_fields, $actual_fields, 'The INDEX field names don\'t match');
                $this->assertEquals(1, $index_info['fields'][$expected_fields[0]]['position'], 'The field position in the INDEX is not correct');
            }
    
            //test INDEX on MULTIPLE FIELDS
            $index_name = 'multipletestindex';
            $index_info = $this->db->reverse->getTableIndexDefinition($this->table, $index_name);
            if (MDB2::isError($index_info)) {
                $this->fail('Error in getTableIndexDefinition(): '.$index_info->getUserInfo());
            } else {
                $this->assertEquals(2, count($index_info['fields']), 'The INDEX is not on two fields unlike it was expected');
                $expected_fields = array_keys($this->indices[$index_name]['fields']);
                $actual_fields = array_keys($index_info['fields']);
                $this->assertEquals($expected_fields, $actual_fields, 'The INDEX field names don\'t match');
                $this->assertEquals(1, $index_info['fields'][$expected_fields[0]]['position'], 'The field position in the INDEX is not correct');
                $this->assertEquals(2, $index_info['fields'][$expected_fields[1]]['position'], 'The field position in the INDEX is not correct');
            }
    
            if (!$this->setUpConstraints()) {
                $this->markTestSkipped('Could not set up constraints.');
            }
            //constraints should NOT be listed
            foreach (array_keys($this->constraints) as $constraint_name) {
                $this->db->expectError(MDB2_ERROR_NOT_FOUND);
                $result = $this->db->reverse->getTableIndexDefinition($this->table, $constraint_name);
                $this->assertTrue(MDB2::isError($result), 'Error listing index definition, this is a CONSTRAINT');
            }
    
            //test index created WITHOUT using MDB2 (i.e. without the "_idx" suffix)
            //NB: MDB2 > v.2.3.0 provides a fallback mechanism
        }
    
        /**
         * Test testGetTableConstraintDefinition($table, $constraint_name)
         * @dataProvider provider
         */
        public function testGetTableConstraintDefinition($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->methodExists($this->db->reverse, 'getTableConstraintDefinition')) {
                $this->markTestSkipped('Driver lacks getTableConstraintDefinition.');
            }
    
            if (!$this->setUpConstraints()) {
                $this->markTestSkipped('Could not set up constraints.');
            }
    
            $primary_namechange = array(
                'mysql',
                'mysqli',
                'sqlite',
            );
    
    
            //test constraint names
            foreach ($this->constraints as $constraint_name => $constraint) {
                if (!empty($constraint['primary'])
                    && in_array($this->db->phptype, $primary_namechange))
                {
                    // Change "pkfield" to "primary".
                    $constraint_name = 'primary';
                }
                $result = $this->db->reverse->getTableConstraintDefinition($this->table, $constraint_name);
                if (MDB2::isError($result)) {
                    $this->fail('Error getting table constraint definition ('.$constraint_name.')');
                } else {
                    $constraint_names = array_keys($constraint['fields']);
                    $this->assertEquals($constraint_names, array_keys($result['fields']), 'Error listing constraint fields');
                }
            }
    
            $this->setUpIndices();
            //indices should NOT be listed
            foreach (array_keys($this->indices) as $index_name) {
                $this->db->expectError(MDB2_ERROR_NOT_FOUND);
                $result = $this->db->reverse->getTableConstraintDefinition($this->table, $index_name);
                $this->db->popExpect();
                $this->assertTrue(MDB2::isError($result), 'Error listing constraint definition, this is a normal INDEX');
            }
    
            //test PK
            if (in_array($this->db->phptype, $primary_namechange)) {
                $constraint_name = 'primary';
            } else {
                $constraint_name = 'pkfield';
            }
            $constraint_info = $this->db->reverse->getTableConstraintDefinition($this->table, $constraint_name);
            if (MDB2::isError($constraint_info)) {
                $this->fail('Error in getTableConstraintDefinition(): '.$constraint_info->getUserInfo());
            } else {
                $this->assertTrue($constraint_info['primary'], 'The field is not a PK unlike it was expected');
            }
    
            //test UNIQUE
            $constraint_name = 'singleunique';
            $constraint_info = $this->db->reverse->getTableConstraintDefinition($this->table, $constraint_name);
            if (MDB2::isError($constraint_info)) {
                $this->fail('Error in getTableConstraintDefinition(): '.$constraint_info->getUserInfo());
            } else {
                $this->assertTrue($constraint_info['unique'], 'The field is not a PK unlike it was expected');
                $this->assertTrue(empty($constraint_info['primary']), 'The field is a PK unlike it was expected');
                $this->assertEquals(1, count($constraint_info['fields']), 'The UNIQUE INDEX is not on one field unlike it was expected');
                $expected_fields = array_keys($this->constraints[$constraint_name]['fields']);
                $actual_fields = array_keys($constraint_info['fields']);
                $this->assertEquals($expected_fields, $actual_fields, 'The UNIQUE INDEX field names don\'t match');
                $this->assertEquals(1, $constraint_info['fields'][$expected_fields[0]]['position'], 'The field position in the INDEX is not correct');
            }
    
            //test UNIQUE on MULTIPLE FIELDS
            $constraint_name = 'multipleunique';
            $constraint_info = $this->db->reverse->getTableConstraintDefinition($this->table, $constraint_name);
            if (MDB2::isError($constraint_info)) {
                $this->fail('Error in getTableConstraintDefinition(): '.$constraint_info->getUserInfo());
            } else {
                $this->assertTrue($constraint_info['unique'], 'The field is not a PK unlike it was expected');
                $this->assertTrue(empty($constraint_info['primary']), 'The field is a PK unlike it was expected');
                $this->assertEquals(2, count($constraint_info['fields']), 'The UNIQUE INDEX is not on two fields unlike it was expected');
                $expected_fields = array_keys($this->constraints[$constraint_name]['fields']);
                $actual_fields = array_keys($constraint_info['fields']);
                $this->assertEquals($expected_fields, $actual_fields, 'The UNIQUE INDEX field names don\'t match');
                $this->assertEquals(1, $constraint_info['fields'][$expected_fields[0]]['position'], 'The field position in the INDEX is not correct');
                $this->assertEquals(2, $constraint_info['fields'][$expected_fields[1]]['position'], 'The field position in the INDEX is not correct');
            }
    
            //test FOREIGN KEYs
            foreach (array_keys($this->constraints2) as $constraint_name) {
                $constraint_info = $this->db->reverse->getTableConstraintDefinition($this->table2, $constraint_name);
                if (MDB2::isError($constraint_info)) {
                    $this->fail('Error in getTableConstraintDefinition():'. $constraint_info->getUserInfo());
                } else {
                    $this->_compareFKdefinitions($this->constraints2[$constraint_name], $constraint_info);
                }
            }
        }
    
        /**
         * Check the original FK constraint definition against the reverse engineered one.
         *
         * Ideally, the retrieved FK constraint definition should be equal to the
         * one used to create the constraint, but not all the DBMS support all the
         * parameters, so check the common base and do some generic checks for the
         * other patameters.
         * @dataProvider provider
         */
        public function _compareFKdefinitions($expected, $actual) {
            //ideal case: all the parameters are supported by all the DBMS:
            //$this->assertEquals($expected, $actual);
    
            $this->assertEquals($expected['primary'], $actual['primary']);
            $this->assertEquals($expected['unique'],  $actual['unique']);
            $this->assertEquals($expected['foreign'], $actual['foreign']);
            $this->assertEquals($expected['check'],   $actual['check']);
            $this->assertEquals(array_keys($expected['fields']), array_keys($actual['fields']));
            $this->assertEquals($expected['references'],   $actual['references']);
            $this->assertEquals($expected['deferrable'],   $actual['deferrable']);
            $this->assertEquals($expected['initiallydeferred'],   $actual['initiallydeferred']);
            $this->assertTrue(!empty($actual['match']));
            $this->assertTrue(!empty($actual['onupdate']));
            $this->assertTrue(!empty($actual['ondelete']));
        }
    
        /**
         * Test getSequenceDefinition($sequence)
         * @dataProvider provider
         */
        public function testGetSequenceDefinition($ci) {
            $this->manualSetUp($ci);
    
            //setup
            $this->db->loadModule('Manager', null, true);
            $sequence = 'test_sequence';
            $sequences = $this->db->manager->listSequences();
            if (!in_array($sequence, $sequences)) {
                $result = $this->db->manager->createSequence($sequence);
                $action = 'create sequence';
                if (MDB2::isError($result)) {
                    if ($result->getCode() == MDB2_ERROR_NO_PERMISSION
                        || $result->getCode() == MDB2_ERROR_ACCESS_VIOLATION)
                    {
                        $this->markTestSkipped("Test user lacks permission to $action");
                    }
                    $this->fail("Could not $action: " . $result->getUserInfo());
                }
            }
    
            //test
            $start = $this->db->nextId($sequence);
            $def = $this->db->reverse->getSequenceDefinition($sequence);
            $this->assertEquals($start+1, (isset($def['start']) ? $def['start'] : 1), 'Error getting sequence definition');
    
            //cleanup
            $result = $this->db->manager->dropSequence($sequence);
            $this->assertFalse(MDB2::isError($result), 'Error dropping a sequence');
        }
    
        /**
         * Test getTriggerDefinition($trigger)
         * @dataProvider provider
         */
        public function testGetTriggerDefinition($ci) {
            $this->manualSetUp($ci);
    
            //setup
            $trigger_name = 'test_trigger';
    
            if (!$this->nonstd) {
                $this->markTestSkipped('No Nonstandard Helper for this phptype.');
            }
    
            $action = 'create trigger';
            $result = $this->nonstd->createTrigger($trigger_name, $this->table);
            if (MDB2::isError($result)) {
                if ($result->getCode() == MDB2_ERROR_NO_PERMISSION
                    || $result->getCode() == MDB2_ERROR_ACCESS_VIOLATION)
                {
                    $this->markTestSkipped("Test user lacks permission to $action");
                }
                $this->fail("Could not $action: " . $result->getUserInfo());
            }
    
            //test
            $def = $this->db->reverse->getTriggerDefinition($trigger_name);
            if (MDB2::isError($def)) {
                $this->fail('getTriggerDefinition: '.$def->getUserInfo());
            } else {
                $this->nonstd->checkTrigger($trigger_name, $this->table, $def);
            }
    
            //cleanup
            $result = $this->nonstd->dropTrigger($trigger_name, $this->table);
            if (MDB2::isError($result)) {
                $this->fail('Error dropping the trigger: '.$result->getUserInfo());
            }
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/Standard/UsageTest.php000066400000000000000000002700461213666021100222570ustar00rootroot00000000000000                                |
    // |          Daniel Convissor                           |
    // +----------------------------------------------------------------------+
    //
    // $Id: UsageTest.php 328146 2012-10-25 20:21:06Z danielc $
    
    require_once dirname(__DIR__) . '/autoload.inc';
    
    class Standard_UsageTest extends Standard_Abstract {
        /**
         * Test typed data storage and retrieval
         *
         * This tests typed data storage and retrieval by executing a single
         * prepared query and then selecting the data back from the database
         * and comparing the results
         *
         * @dataProvider provider
         */
        public function testStorage($ci) {
            $this->manualSetUp($ci);
    
            $data = $this->getSampleData(1234);
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
            $result = $stmt->execute(array_values($data));
            $stmt->free();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: ' . $result->getUserInfo());
            }
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->query($query, $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: ' . $result->getUserInfo());
            }
    
            $this->verifyFetchedValues($result, 0, $data);
            $result->free();
        }
    
        /**
         * Test fetchOne()
         *
         * This test bulk fetching of result data by using a prepared query to
         * insert an number of rows of data and then retrieving the data columns
         * one by one
         *
         * @dataProvider provider
         */
        public function testFetchOne($ci) {
            $this->manualSetUp($ci);
    
            $data = array();
            $total_rows = 5;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
    
                if (MDB2::isError($result)) {
                    @$stmt->free();
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
            }
    
            $stmt->free();
    
            foreach ($this->fields as $field => $type) {
                for ($row = 0; $row < $total_rows; $row++) {
                    $result = $this->db->query('SELECT '.$field.' FROM ' . $this->table_users . ' WHERE user_id='.$row, $type);
                    $value = $result->fetchOne();
                    $result->free();
                    if (MDB2::isError($value)) {
                        $this->fail('Error fetching row '.$row.' for field '.$field.' of type '.$type);
                    }
                    $this->assertEquals(strval($data[$row][$field]), strval(trim($value)), 'the query field '.$field.' of type '.$type.' for row '.$row);
                }
            }
        }
    
        /**
         * Test fetchRow()
         *
         * Touch on issues raised in bugs 9502, 18203, 19303.
         *
         * @dataProvider provider
         */
        public function testFetchRow($ci) {
            $this->manualSetUp($ci);
    
            $data = array();
            $total_rows = 1;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 1; $row <= $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
    
                if (MDB2::isError($result)) {
                    @$stmt->free();
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
            }
    
            $stmt->free();
    
            $query = "INSERT INTO $this->table_files (id) VALUES (1)";
            $result = $this->db->exec($query);
            if (MDB2::isError($result)) {
                $this->fail('Error inserting files: ' . $result->getUserInfo());
            }
    
            if ($this->db->phptype == 'oci8') {
                $as = '';
            } else {
                $as = 'AS';
            }
    
            $query = "SELECT u.user_name, u.user_id $as id, f.id
                FROM $this->table_users AS u
                LEFT JOIN $this->table_files AS f ON (u.user_id = f.id)
                ORDER BY user_name";
    
            $expect_obj_type = new stdClass;
            $expect_obj_type->user_name = 'user_1';
            $expect_obj_type->id = 1;
    
            $expect_obj_string = new stdClass;
            $expect_obj_string->user_name = 'user_1';
            $expect_obj_string->id = '1';
    
            $expect_assoc_type = (array) $expect_obj_type;
            $expect_assoc_string = (array) $expect_obj_string;
            $expect_enum_type = array_values($expect_assoc_type);
            $expect_enum_type[] = 1;
            $expect_enum_string = array_values($expect_assoc_string);
            $expect_enum_string[] = '1';
    
            // Untyped.
            $result = $this->db->query($query);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting: ' . $result->getUserInfo());
            }
            $values = $result->fetchRow(MDB2_FETCHMODE_ORDERED);
            $this->assertSame($expect_enum_string, $values, 'untyped, ordered');
    
            $result = $this->db->query($query);
            $values = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
            $this->assertSame($expect_assoc_string, $values, 'untyped, assoc');
    
            $result = $this->db->query($query);
            $values = $result->fetchRow(MDB2_FETCHMODE_OBJECT);
            // Need to compare strings because they're different objects.
            $this->assertSame(var_export($expect_obj_string, true),
                var_export($values, true), 'untyped, object');
    
            // Enum typed.
            $types = array('text', 'integer', 'integer');
    
            $result = $this->db->query($query, $types);
            $values = $result->fetchRow(MDB2_FETCHMODE_ORDERED);
            $this->assertSame($expect_enum_type, $values, 'enum typed, ordered');
    
    # Unsupported...
    #        $result = $this->db->query($query, $types);
    #        $values = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
    #        $this->assertSame($expect_assoc_type, $values, 'enum typed, assoc');
    #
    #        $result = $this->db->query($query, $types);
    #        $values = $result->fetchRow(MDB2_FETCHMODE_OBJECT);
    #        // Need to compare strings because they're different objects.
    #        $this->assertSame(var_export($expect_obj_type, true),
    #            var_export($values, true), 'enum typed, object');
    
            // Assoc typed.
            $types = array('user_name' => 'text', 'id' => 'integer');
    
    # Unsupported...
    #        $result = $this->db->query($query, $types);
    #        $values = $result->fetchRow(MDB2_FETCHMODE_ORDERED);
    #        $this->assertSame($expect_enum_type, $values, 'assoc typed, ordered');
    
            $result = $this->db->query($query, $types);
            $values = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
            $this->assertSame($expect_assoc_type, $values, 'assoc typed, assoc');
    
            $result = $this->db->query($query, $types);
            $values = $result->fetchRow(MDB2_FETCHMODE_OBJECT);
            // Need to compare strings because they're different objects.
            $this->assertSame(var_export($expect_obj_type, true),
                var_export($values, true), 'assoc typed, object');
        }
    
        /**
         * Test fetchCol()
         *
         * Test fetching a column of result data. Two different columns are retrieved
         *
         * @dataProvider provider
         */
        public function testFetchCol($ci) {
            $this->manualSetUp($ci);
    
            $data = array();
            $total_rows = 5;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
                if (MDB2::isError($result)) {
                    @$stmt->free();
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
            }
    
            $stmt->free();
    
            $first_col = array();
            for ($row = 0; $row < $total_rows; $row++) {
                $first_col[$row] = "user_$row";
            }
    
            $second_col = array();
            for ($row = 0; $row < $total_rows; $row++) {
                $second_col[$row] = $row;
            }
    
            $query = 'SELECT user_name, user_id FROM ' . $this->table_users . ' ORDER BY user_name';
            $result = $this->db->query($query, array('text', 'integer'));
            if (MDB2::isError($result)) {
                $this->fail('Error during query: ' . $result->getUserInfo());
            }
            $values = $result->fetchCol(0);
            $result->free();
            if (MDB2::isError($values)) {
                $this->fail('Error fetching first column');
            }
            $this->assertEquals($first_col, $values);
    
            $query = 'SELECT user_name, user_id FROM ' . $this->table_users . ' ORDER BY user_name';
            $result = $this->db->query($query, array('text', 'integer'));
            if (MDB2::isError($result)) {
                $this->fail('Error during query: ' . $result->getUserInfo());
            }
            $values = $result->fetchCol(1);
            $result->free();
            if (MDB2::isError($values)) {
                $this->fail('Error fetching second column');
            }
            $this->assertEquals($second_col, $values);
        }
    
        /**
         * Test fetchAll()
         *
         * Test fetching an entire result set in one shot.
         *
         * @dataProvider provider
         */
        public function testFetchAll($ci) {
            $this->manualSetUp($ci);
    
            $data = array();
            $total_rows = 5;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
                if (MDB2::isError($result)) {
                    @$stmt->free();
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
            }
            $stmt->free();
    
            $fields = array_keys($data[0]);
            $query = 'SELECT '. implode (', ', $fields). ' FROM ' . $this->table_users . ' ORDER BY user_name';
            $result = $this->db->query($query, $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error during query: '  . $result->getUserInfo());
            }
            $values = $result->fetchAll(MDB2_FETCHMODE_ASSOC);
            $result->free();
            if (MDB2::isError($values)) {
                $this->fail('Error fetching the result set');
            }
            for ($i=0; $i<$total_rows; $i++) {
                foreach ($data[$i] as $key => $val) {
                    $this->assertEquals(strval($val), strval($values[$i][$key]), 'Row #'.$i.' ['.$key.']');
                }
            }
    
            //test $rekey=true
            $result = $this->db->query('SELECT user_id, user_name FROM ' . $this->table_users . ' ORDER BY user_id', $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error during query: ' . $result->getUserInfo());
            }
            $values = $result->fetchAll(MDB2_FETCHMODE_ASSOC, true);
            $result->free();
            if (MDB2::isError($values)) {
                $this->fail('Error fetching the result set');
            }
            for ($i=0; $i<$total_rows; $i++) {
                list($id, $name) = each($values);
                $this->assertEquals($data[$i]['user_id'],   $id,   'Row #'.$i.' ["user_id"]');
                $this->assertEquals($data[$i]['user_name'], $name, 'Row #'.$i.' ["user_name"]');
            }
    
    
            //test $rekey=true, $force_array=true
            $result = $this->db->query('SELECT user_id, user_name FROM ' . $this->table_users . ' ORDER BY user_id', $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error during query: ' . $result->getUserInfo());
            }
            $values = $result->fetchAll(MDB2_FETCHMODE_ASSOC, true, true);
            $result->free();
            if (MDB2::isError($values)) {
                $this->fail('Error fetching the result set');
            }
            for ($i=0; $i<$total_rows; $i++) {
                list($id, $value) = each($values);
                $this->assertEquals($data[$i]['user_id'],   $id,                 'Row #'.$i.' ["user_id"]');
                $this->assertEquals($data[$i]['user_name'], $value['user_name'], 'Row #'.$i.' ["user_name"]');
            }
    
            //test $rekey=true, $force_array=true, $group=true
            $result = $this->db->query('SELECT user_password, user_name FROM ' . $this->table_users . ' ORDER BY user_name', $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error during query: ' . $result->getUserInfo());
            }
            $values = $result->fetchAll(MDB2_FETCHMODE_ASSOC, true, true, true);
            $result->free();
            if (MDB2::isError($values)) {
                $this->fail('Error fetching the result set');
            }
            //all the records have the same user_password value
            $this->assertEquals(1, count($values), 'Error: incorrect number of returned rows');
            $values = $values[$data[0]['user_password']];
            for ($i=0; $i<$total_rows; $i++) {
                $this->assertEquals($data[$i]['user_name'], $values[$i]['user_name'], 'Row #'.$i.' ["user_name"]');
            }
    
            //test $rekey=true, $force_array=true, $group=false (with non unique key)
            $result = $this->db->query('SELECT user_password, user_name FROM ' . $this->table_users . ' ORDER BY user_name', $this->fields);
            if (MDB2::isError($result)) {
                $this->fail('Error during query: ' . $result->getUserInfo());
            }
            $values = $result->fetchAll(MDB2_FETCHMODE_ASSOC, true, true, false);
            $result->free();
            if (MDB2::isError($values)) {
                $this->fail('Error fetching the result set');
            }
            //all the records have the same user_password value, they are overwritten
            $this->assertEquals(1, count($values), 'Error: incorrect number of returned rows');
            $key = $data[0]['user_password'];
            $this->assertEquals(1, count($values[$key]), 'Error: incorrect number of returned rows');
            $this->assertEquals($data[4]['user_name'], $values[$key]['user_name']);
        }
    
        /**
         * Test different fetch modes
         *
         * Test fetching results using different fetch modes
         * NOTE: several tests still missing
         *
         * @dataProvider provider
         */
        public function testFetchModes($ci) {
            $this->manualSetUp($ci);
    
            $data = array();
            $total_rows = 5;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
    
                if (MDB2::isError($result)) {
                    @$stmt->free();
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
            }
    
            $stmt->free();
    
            // test ASSOC
            $query = 'SELECT A.user_name FROM ' . $this->table_users . ' A, ' . $this->table_users . ' B WHERE A.user_id = B.user_id';
            $value = $this->db->queryRow($query, array($this->fields['user_name']), MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($value)) {
                $this->fail('Error fetching the result set');
            }
            $this->assertTrue(!empty($value['user_name']), 'Error fetching the associative result set from join');
        }
    
        /**
         * Test multi_query option
         *
         * This test attempts to send multiple queries at once using the multi_query
         * option and then retrieves each result.
         *
         * @dataProvider provider
         */
        public function testMultiQuery($ci) {
            $this->manualSetUp($ci);
    
            $multi_query_orig = $this->db->getOption('multi_query');
            if (MDB2::isError($multi_query_orig) && ($multi_query_orig->getCode() == MDB2_ERROR_UNSUPPORTED)) {
                $this->markTestSkipped('Multi query not supported');
            }
    
            $this->db->setOption('multi_query', true);
    
            $data = array();
            $total_rows = 5;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
    
                if (MDB2::isError($result)) {
                    @$stmt->free();
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
            }
    
            $stmt->free();
    
            $query = '';
            for ($row = 0; $row < $total_rows; $row++) {
                $query.= 'SELECT user_name FROM ' . $this->table_users . ' WHERE user_id='.$row.';';
            }
            $result = $this->db->query($query, 'text');
    
            for ($row = 0; $row < $total_rows; $row++) {
                $value = $result->fetchOne();
                if (MDB2::isError($value)) {
                    $this->fail('Error fetching row '.$row);
                }
                $this->assertEquals(strval($data[$row]['user_name']), strval(trim($value)), 'the query field username of type "text" for row '.$row);
                if (MDB2::isError($result->nextResult())) {
                    $this->fail('Error moving result pointer');
                }
            }
    
            $result->free();
        }
    
        /**
         * Test prepared queries
         *
         * Tests prepared queries, making sure they correctly deal with ?, !, and '
         *
         * @dataProvider provider
         */
        public function testPreparedQueries($ci) {
            $this->manualSetUp($ci);
    
            $data = array(
                array(
                    'user_name' => 'Sure!',
                    'user_password' => 'Do work?',
                    'user_id' => 1,
                ),
                array(
                    'user_name' => 'For Sure!',
                    'user_password' => "Doesn't?",
                    'user_id' => 2,
                ),
            );
    
            $query = "INSERT INTO $this->table_users (user_name, user_password, user_id) VALUES (?, ?, ?)";
            $stmt = $this->db->prepare($query, array('text', 'text', 'integer'), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $text = $data[0]['user_name'];
            $question = $data[0]['user_password'];
            $userid = $data[0]['user_id'];
    
            // bind out of order
            $stmt->bindParam(0, $text);
            $stmt->bindParam(2, $userid);
            $stmt->bindParam(1, $question);
    
            $result = $stmt->execute();
            if (MDB2::isError($result)) {
                @$stmt->free();
                $this->fail('Could not execute prepared query with question mark placeholders. Error: ' . $result->getUserInfo());
            }
    
            $text = $data[1]['user_name'];
            $question = $data[1]['user_password'];
            $userid = $data[1]['user_id'];
    
            $result = $stmt->execute();
            $stmt->free();
            if (MDB2::isError($result)) {
                $this->fail('Could not execute prepared query with bound parameters. Error: ' . $result->getUserInfo());
            }
            $this->clearTables();
    
            $query = "INSERT INTO $this->table_users (user_name, user_password, user_id) VALUES (:text, :question, :userid)";
            $stmt = $this->db->prepare($query, array('text', 'text', 'integer'), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $stmt->bindValue('text', $data[0]['user_name']);
            $stmt->bindValue('question', $data[0]['user_password']);
            $stmt->bindValue('userid', $data[0]['user_id']);
    
            $result = $stmt->execute();
            $stmt->free();
            if (MDB2::isError($result)) {
                $this->fail('Could not execute prepared query with named placeholders. Error: ' . $result->getUserInfo());
            }
    
            $query = "INSERT INTO $this->table_users (user_name, user_password, user_id) VALUES (".$this->db->quote($data[1]['user_name'], 'text').", :question, :userid)";
            $stmt = $this->db->prepare($query, array('text', 'integer'), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $stmt->bindValue('question', $data[1]['user_password']);
            $stmt->bindValue('userid', $data[1]['user_id']);
    
            $result = $stmt->execute();
            $stmt->free();
            if (MDB2::isError($result)) {
                $this->fail('Could not execute prepared query with named placeholders and a quoted text value in front. Error: ' . $result->getUserInfo());
            }
    
            $query = 'SELECT user_name, user_password, user_id FROM ' . $this->table_users . ' WHERE user_id=:user_id';
            $stmt = $this->db->prepare($query, array('integer'), array('text', 'text', 'integer'));
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
            foreach ($data as $row_data) {
                $result = $stmt->execute(array('user_id' => $row_data['user_id']));
                if (MDB2::isError($result)) {
                    @$stmt->free();
                    $this->fail('Could not execute prepared. Error: '.$result->getUserinfo());
                }
                $row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
                if (!is_array($row)) {
                    @$result->free();
                    @$stmt->free();
                    $this->fail('Prepared SELECT failed');
                }
                $diff = (array)array_diff($row, $row_data);
                $this->assertTrue(empty($diff), 'Prepared SELECT failed for fields: '.implode(', ', array_keys($diff)));
            }
            $result->free();
            $stmt->free();
    
            $row_data = reset($data);
            $query = 'SELECT user_name, user_password, user_id FROM ' . $this->table_users . ' WHERE user_id='.$this->db->quote($row_data['user_id'], 'integer');
            $stmt = $this->db->prepare($query, null, array('text', 'text', 'integer'));
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
            $result = $stmt->execute(array());
            $stmt->free();
            if (MDB2::isError($result)) {
                $this->fail('Could not execute prepared statement with no placeholders. Error: '.$result->getUserinfo());
            }
            $row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
            if (!is_array($row)) {
                @$result->free();
                $this->fail('Prepared SELECT failed');
            }
            $diff = (array)array_diff($row, $row_data);
            $this->assertTrue(empty($diff), 'Prepared SELECT failed for fields: '.implode(', ', array_keys($diff)));
            $stmt->free();
    
            $row_data = reset($data);
            $query = 'SELECT user_name, user_password, user_id FROM ' . $this->table_users . ' WHERE user_name='.$this->db->quote($row_data['user_name'], 'text').' AND user_id = ? AND user_password='.$this->db->quote($row_data['user_password'], 'text');
            $stmt = $this->db->prepare($query, array('integer'), array('text', 'text', 'integer'));
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
            $result = $stmt->execute(array($row_data['user_id']));
            $stmt->free();
            if (MDB2::isError($result)) {
                $this->fail('Could not execute prepared with quoted text fields around a placeholder. Error: '.$result->getUserinfo());
            }
            $row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
            $result->free();
            if (!is_array($row)) {
                $this->fail('Prepared SELECT failed');
            }
            $diff = (array)array_diff($row, $row_data);
            $this->assertTrue(empty($diff), 'Prepared SELECT failed for fields: '.implode(', ', array_keys($diff)));
    
            foreach ($this->db->sql_comments as $comment) {
                $query = 'SELECT user_name, user_password, user_id FROM ' . $this->table_users . ' WHERE '.$comment['start'].' maps to class::foo() '.$comment['end'].' user_name=:username';
                $row_data = reset($data);
                $stmt = $this->db->prepare($query, array('text'), array('text', 'text', 'integer'));
                if (MDB2::isError($stmt)) {
                    $this->fail('Error preparing query: ' . $stmt->getUserInfo());
                }
                $result = $stmt->execute(array('username' => $row_data['user_name']));
                $stmt->free();
                if (MDB2::isError($result)) {
                    $this->fail('Could not execute prepared where a name parameter is contained in an SQL comment ('.$comment['start'].'). Error: '.$result->getUserinfo());
                }
                $row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
                $result->free();
                if (!is_array($row)) {
                    $this->fail('Prepared SELECT failed');
                }
                $diff = (array)array_diff($row, $row_data);
                $this->assertTrue(empty($diff), 'Prepared SELECT failed for fields: '.implode(', ', array_keys($diff)));
            }
    
            $row_data = reset($data);
            $query = 'SELECT user_name, user_password, user_id FROM ' . $this->table_users . ' WHERE user_name=:username OR user_password=:username';
            $stmt = $this->db->prepare($query, array('text'), array('text', 'text', 'integer'));
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
            $result = $stmt->execute(array('username' => $row_data['user_name']));
            $stmt->free();
            if (MDB2::isError($result)) {
                $this->fail('Could not execute prepared where the same named parameter is used twice. Error: '.$result->getUserinfo());
            }
            $row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
            $result->free();
            if (!is_array($row)) {
                $this->fail('Prepared SELECT failed');
            }
            $diff = (array)array_diff($row, $row_data);
            $this->assertTrue(empty($diff), 'Prepared SELECT failed for fields: '.implode(', ', array_keys($diff)));
        }
    
    
    // TODO:  go through rest of file fixing order of execution & freeing resources.
    
    
        /**
         * Test _skipDelimitedStrings(), used by prepare()
         *
         * If the placeholder is contained within a delimited string, it must be skipped,
         * and the cursor position must be advanced
         *
         * @dataProvider provider
         */
        public function testSkipDelimitedStrings($ci) {
            $this->manualSetUp($ci);
    
            //test correct placeholder
            $query = 'SELECT what FROM tbl WHERE x = ?';
            $position = 0;
            $p_position = strpos($query, '?');
            $this->assertEquals($position, $this->db->_skipDelimitedStrings($query, $position, $p_position), 'Error: the cursor position has changed');
    
            //test placeholder within a quoted string
            $query = 'SELECT what FROM tbl WHERE x = '. $this->db->string_quoting['start'] .'blah?blah'. $this->db->string_quoting['end'] .' AND y = ?';
            $position = 0;
            $p_position = strpos($query, '?');
            $new_pos = $this->db->_skipDelimitedStrings($query, $position, $p_position);
            $this->assertTrue($position !=$new_pos, 'Error: the cursor position was not advanced');
    
            //test placeholder within a comment
            foreach ($this->db->sql_comments as $comment) {
                $query = 'SELECT what FROM tbl WHERE x = '. $comment['start'] .'blah?blah'. $comment['end'] .' AND y = ?';
                $position = 0;
                $p_position = strpos($query, '?');
                $new_pos = $this->db->_skipDelimitedStrings($query, $position, $p_position);
                $this->assertTrue($position != $new_pos, 'Error: the cursor position was not advanced');
            }
    
            // bug 17039: http://pear.php.net/bugs/17039
            $query = "SELECT 'a\'b:+c'";
            $position = 0;
            $p_position = strpos($query, ':');
            $new_pos = $this->db->_skipDelimitedStrings($query, $position, $p_position);
            $this->assertTrue($position != $new_pos, 'Error: the cursor position was not advanced');
    
            // bug 16973: http://pear.php.net/bugs/16973
            if ($this->db->supports('prepared_statements') != 'emulated') {
                $this->db->expectError(MDB2_ERROR_SYNTAX);
                $query = " select '?\\' ";
                $stmt = $this->db->prepare($query);
                $this->assertTrue(MDB2::isError($stmt), 'Expected Exception query with an unterminated text string specified');
                $this->db->popExpect();
    
                $query = " select 'a\\'?\\'' ";
                $stmt = $this->db->prepare($query);
                $this->assertFalse(MDB2::isError($stmt));
    
                $query = " select concat('\\\\\\\\', ?) ";
                $stmt = $this->db->prepare($query);
                $this->assertFalse(MDB2::isError($stmt));
    
                $this->db->expectError(MDB2_ERROR_SYNTAX);
                $query = " select concat('\\\\\\\\\\', ?) ";
                $stmt = $this->db->prepare($query);
                $this->assertTrue(MDB2::isError($stmt), 'Expected Exception query with an unterminated text string specified');
                $this->db->popExpect();
            }
    
            //add some tests for named placeholders and for identifier_quoting
        }
    
        /**
         * Test retrieval of result metadata
         *
         * This tests the result metadata by executing a prepared query and
         * select the data, and checking the result contains the correct
         * number of columns and that the column names are in the correct order
         *
         * @dataProvider provider
         */
        public function testMetadata($ci) {
            $this->manualSetUp($ci);
    
            $data = $this->getSampleData(1234);
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $result = $stmt->execute(array_values($data));
            $stmt->free();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: ' . $result->getUserInfo());
            }
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->query($query, $this->fields);
    
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: ' . $result->getUserInfo());
            }
    
            $numcols = $result->numCols();
    
            $this->assertEquals(count($this->fields), $numcols, "The query result returned an incorrect number of columns unlike expected");
    
            $column_names = $result->getColumnNames();
            $fields = array_keys($this->fields);
            for ($column = 0; $column < $numcols; $column++) {
                $this->assertEquals($column, $column_names[$fields[$column]], "The query result column \"".$fields[$column]."\" was returned in an incorrect position");
            }
    
        }
    
        /**
         * Test storage and retrieval of nulls
         *
         * This tests null storage and retrieval by successively inserting,
         * selecting, and testing a number of null / not null values
         *
         * @dataProvider provider
         */
        public function testNulls($ci) {
            $this->manualSetUp($ci);
    
            $portability = $this->db->getOption('portability');
            if ($portability & MDB2_PORTABILITY_EMPTY_TO_NULL) {
                $nullisempty = true;
            } else {
                $nullisempty = false;
            }
            $test_values = array(
                array('test', false),
                array('NULL', false),
                array('null', false),
                array('', $nullisempty),
                array(null, true)
            );
    
            for ($test_value = 0; $test_value <= count($test_values); $test_value++) {
                if ($test_value == count($test_values)) {
                    $value = 'NULL';
                    $is_null = true;
                } else {
                    $value = $this->db->quote($test_values[$test_value][0], 'text');
                    $is_null = $test_values[$test_value][1];
                }
    
                $this->clearTables();
    
                $result = $this->db->exec("INSERT INTO $this->table_users (user_name,user_password,user_id) VALUES ($value,$value,0)");
                if (MDB2::isError($result)) {
                    $this->fail('Error executing insert query: ' . $result->getUserInfo());
                }
    
                $result = $this->db->query('SELECT user_name,user_password FROM ' . $this->table_users, array('text', 'text'));
                if (MDB2::isError($result)) {
                    $this->fail('Error executing select query: ' . $result->getUserInfo());
                }
    
                if ($is_null) {
                    $error_message = 'A query result column is not NULL unlike what was expected';
                } else {
                    $error_message = 'A query result column is NULL even though it was expected to be different';
                }
    
                $row = $result->fetchRow();
                $result->free();
                $this->assertTrue((is_null($row[0]) == $is_null), $error_message);
                $this->assertTrue((is_null($row[1]) == $is_null), $error_message);
            }
    
            $methods = array('fetchOne', 'fetchRow');
    
            foreach ($methods as $method) {
                $result = $this->db->query('SELECT user_name FROM ' . $this->table_users . ' WHERE user_id=123', array('text'));
                $value = $result->$method();
                if (MDB2::isError($value)) {
                    $this->fail('Error fetching non existent row');
                } else {
                    $result->free();
                    $this->assertNull($value, 'selecting non existent row with "'.$method.'()" did not return NULL');
                }
            }
    
            $methods = array('fetchCol', 'fetchAll');
    
            foreach ($methods as $method) {
                $result = $this->db->query('SELECT user_name FROM ' . $this->table_users . ' WHERE user_id=123', array('text'));
                $value = $result->$method();
                if (MDB2::isError($value)) {
                    $this->fail('Error fetching non existent row');
                } else {
                    $result->free();
                    $this->assertTrue((is_array($value) && empty($value)), 'selecting non existent row with "'.$method.'()" did not return empty array');
                }
            }
    
            $methods = array('queryOne', 'queryRow');
    
            foreach ($methods as $method) {
                $value = $this->db->$method('SELECT user_name FROM ' . $this->table_users . ' WHERE user_id=123', array('text'));
                if (MDB2::isError($value)) {
                    $this->fail('Error fetching non existent row');
                } else {
                    $result->free();
                    $this->assertNull($value, 'selecting non existent row with "'.$method.'()" did not return NULL');
                }
            }
    
            $methods = array('queryCol', 'queryAll');
    
            foreach ($methods as $method) {
                $value = $this->db->$method('SELECT user_name FROM ' . $this->table_users . ' WHERE user_id=123', array('text'));
                if (MDB2::isError($value)) {
                    $this->fail('Error fetching non existent row');
                } else {
                    $result->free();
                    $this->assertTrue((is_array($value) && empty($value)), 'selecting non existent row with "'.$method.'()" did not return empty array');
                }
            }
        }
    
        /**
         * Test paged queries
         *
         * Test the use of setLimit to return paged queries
         *
         * @dataProvider provider
         */
        public function testRanges($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('limit_queries')) {
                $this->markTestSkipped('LIMIT not supported');
            }
    
            $data = array();
            $total_rows = 5;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
    
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
            }
    
            $stmt->free();
    
            for ($rows = 2, $start_row = 0; $start_row < $total_rows; $start_row += $rows) {
    
                $this->db->setLimit($rows, $start_row);
    
                $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users . ' ORDER BY user_name';
                $result = $this->db->query($query, $this->fields);
    
                if (MDB2::isError($result)) {
                    $this->fail('Error executing select query: ' . $result->getUserInfo());
                }
    
                for ($row = 0; $row < $rows && ($row + $start_row < $total_rows); $row++) {
                    $this->verifyFetchedValues($result, $row, $data[$row + $start_row]);
                }
            }
    
            $this->assertFalse($result->valid(), "The query result did not seem to have reached the end of result as expected starting row $start_row after fetching upto row $row");
    
            $result->free();
    
            for ($rows = 2, $start_row = 0; $start_row < $total_rows; $start_row += $rows) {
    
                $this->db->setLimit($rows, $start_row);
    
                $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users . ' ORDER BY user_name';
                $result = $this->db->query($query, $this->fields);
    
                if (MDB2::isError($result)) {
                    $this->fail('Error executing select query: ' . $result->getUserInfo());
                }
    
                $result_rows = $result->numRows();
    
                $expected = ($start_row == ($total_rows-1)) ? 1 : $rows;
                $this->assertEquals($expected, $result_rows, 'invalid number of rows returned');
                $this->assertTrue(($result_rows <= $rows), 'expected a result of no more than '.$rows.' but the returned number of rows is '.$result_rows);
    
                for ($row = 0; $row < $result_rows; $row++) {
                    $this->assertTrue($result->valid(), 'The query result seem to have reached the end of result at row '.$row.' that is before '.$result_rows.' as expected');
                    $this->verifyFetchedValues($result, $row, $data[$row + $start_row]);
                }
            }
    
            $this->assertTrue(!$result->valid(), "The query result did not seem to have reached the end of result as expected starting row $start_row after fetching upto row $row");
    
            $result->free();
        }
    
        /**
         * Test the handling of sequences
         *
         * @dataProvider provider
         */
        public function testSequences($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('sequences')) {
               $this->markTestSkipped('SEQUENCEs not supported');
            }
    
            $this->db->loadModule('Manager', null, true);
    
            for ($start_value = 1; $start_value < 4; $start_value++) {
                $sequence_name = "test_sequence_{$start_value}";
    
                @$this->db->manager->dropSequence($sequence_name);
                $result = $this->db->manager->createSequence($sequence_name, $start_value);
                if (MDB2::isError($result)) {
                    $this->fail("Error creating sequence $sequence_name with start value $start_value: " . $result->getUserInfo());
                } else {
                    for ($sequence_value = $start_value; $sequence_value < ($start_value + 4); $sequence_value++) {
                        $value = $this->db->nextID($sequence_name, false);
    
                        $this->assertEquals($sequence_value, $value, "The returned sequence value for $sequence_name is not expected with sequence start value with $start_value");
                    }
    
                    $result = $this->db->manager->dropSequence($sequence_name);
    
                    if (MDB2::isError($result)) {
                        $this->fail("Error dropping sequence $sequence_name : " . $result->getUserInfo());
                    }
                }
            }
    
            // Test ondemand creation of sequences
            $sequence_name = 'test_ondemand';
            $this->db->pushErrorHandling(PEAR_ERROR_RETURN);
            $this->db->expectError(MDB2_ERROR_NOSUCHTABLE);
            $this->db->manager->dropSequence($sequence_name);
            $this->db->popExpect();
            $this->db->popErrorHandling();
    
            for ($sequence_value = 1; $sequence_value < 4; $sequence_value++) {
                $value = $this->db->nextID($sequence_name);
    
                if (MDB2::isError($result)) {
                    $this->fail("Error creating with ondemand sequence: " . $result->getUserInfo());
                } else {
                    $this->assertEquals($sequence_value, $value, "Error in ondemand sequences. The returned sequence value is not expected value");
                }
            }
    
            $result = $this->db->manager->dropSequence($sequence_name);
            if (MDB2::isError($result)) {
                $this->fail("Error dropping sequence $sequence_name : " . $result->getUserInfo());
            }
    
            // Test currId()
            $sequence_name = 'test_currid';
    
            $next = $this->db->nextID($sequence_name);
            $curr = $this->db->currID($sequence_name);
    
            if (MDB2::isError($curr)) {
                $this->fail("Error getting the current value of sequence $sequence_name : ".$curr->getMessage());
            } else {
                if ($next != $curr) {
                    if ($next+1 != $curr) {
                        $this->assertEquals($next, $curr, "return value if currID() does not match the previous call to nextID()");
                    }
                }
            }
            $result = $this->db->manager->dropSequence($sequence_name);
            if (MDB2::isError($result)) {
                $this->fail("Error dropping sequence $sequence_name : " . $result->getUserInfo());
            }
    
            // Test lastInsertid()
            if (!$this->db->supports('new_link')) {
               $this->markTestSkipped('Driver does not support new link.');
            }
    
            $sequence_name = 'test_lastinsertid';
    
            $dsn = MDB2::parseDSN($this->dsn);
            $dsn['new_link'] = true;
            $dsn['database'] = $this->database;
            $db = MDB2::connect($dsn, $this->options);
    
            $next = $this->db->nextID($sequence_name);
            $next2 = $db->nextID($sequence_name);
            $last = $this->db->lastInsertID($sequence_name);
    
            if (MDB2::isError($last)) {
                $this->fail("Error getting the last value of sequence $sequence_name : ".$last->getMessage());
            } else {
                $this->assertEquals($next, $last, "return value if lastInsertID() does not match the previous call to nextID()");
            }
            $result = $this->db->manager->dropSequence($sequence_name);
            if (MDB2::isError($result)) {
                $this->fail("Error dropping sequence $sequence_name : " . $result->getUserInfo());
            }
        }
    
        /**
         * Test replace query
         *
         * The replace method emulates the replace query of mysql
         *
         * @dataProvider provider
         */
        public function testReplace($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('replace')) {
                $this->markTestSkipped('REPLACE not supported');
            }
    
            $row = 1234;
            $data = $this->getSampleData($row);
    
            $fields = array(
                'user_name' => array(
                    'value' => "user_$row",
                    'type' => 'text'
                ),
                'user_password' => array(
                    'value' => $data['user_password'],
                    'type' => 'text'
                ),
                'subscribed' => array(
                    'value' => $data['subscribed'],
                    'type' => 'boolean'
                ),
                'user_id' => array(
                    'value' => $data['user_id'],
                    'type' => 'integer',
                    'key' => 1
                ),
                'quota' => array(
                    'value' => $data['quota'],
                    'type' => 'decimal'
                ),
                'weight' => array(
                    'value' => $data['weight'],
                    'type' => 'float'
                ),
                'access_date' => array(
                    'value' => $data['access_date'],
                    'type' => 'date'
                ),
                'access_time' => array(
                    'value' => $data['access_time'],
                    'type' => 'time'
                ),
                'approved' => array(
                    'value' => $data['approved'],
                    'type' => 'timestamp'
                )
            );
    
            $result = $this->db->replace($this->table_users, $fields);
    
            if (MDB2::isError($result)) {
                $this->fail('Replace failed');
            }
            if ($this->db->supports('affected_rows')) {
                $this->assertEquals(1, $result, "replacing a row in an empty table returned incorrect value");
            }
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->query($query, $this->fields);
    
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users' . $result->getUserInfo());
            }
    
            $this->verifyFetchedValues($result, 0, $data);
    
            $row = 4321;
            $fields['user_name']['value']     = $data['user_name']     = 'user_'.$row;
            $fields['user_password']['value'] = $data['user_password'] = 'somepass';
            $fields['subscribed']['value']    = $data['subscribed']    = $row % 2 ? true : false;
            $fields['quota']['value']         = $data['quota']         = strval($row/100);
            $fields['weight']['value']        = $data['weight']        = sqrt($row);
            $fields['access_date']['value']   = $data['access_date']   = MDB2_Date::mdbToday();
            $fields['access_time']['value']   = $data['access_time']   = MDB2_Date::mdbTime();
            $fields['approved']['value']      = $data['approved']      = MDB2_Date::mdbNow();
    
            $result = $this->db->replace($this->table_users, $fields);
    
            if (MDB2::isError($result)) {
                $this->fail('Replace failed');
            }
    
            if ($this->db->supports('affected_rows')) {
                switch ($this->db->phptype) {
                    case 'sqlite':
                        $expect = 1;
                        break;
                    default:
                        $expect = 2;
                }
                $this->assertEquals($expect, $result, "replacing a row returned incorrect result");
            }
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->query($query, $this->fields);
    
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users' . $result->getUserInfo());
            }
    
            $this->verifyFetchedValues($result, 0, $data);
    
            $this->assertTrue(!$result->valid(), 'the query result did not seem to have reached the end of result as expected');
    
            $result->free();
        }
    
        /**
         * Test affected rows methods
         *
         * @dataProvider provider
         */
        public function testAffectedRows($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('affected_rows')) {
                $this->markTestSkipped('Affected rows not supported');
            }
    
            $data = array();
            $total_rows = 7;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
    
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
    
                $this->assertEquals(1, $result, "Inserting the row $row returned incorrect affected row count");
            }
    
            $stmt->free();
    
            $query = 'UPDATE ' . $this->table_users . ' SET user_password=? WHERE user_id < ?';
            $stmt = $this->db->prepare($query, array('text', 'integer'), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 0; $row < $total_rows; $row++) {
                $password = "pass_$row";
                if ($row == 0) {
                    $stmt->bindParam(0, $password);
                    $stmt->bindParam(1, $row);
                }
    
                $result = $stmt->execute();
    
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
    
                $this->assertEquals($row, $result, "Updating the $row rows returned incorrect affected row count");
            }
    
            $stmt->free();
    
            $query = 'DELETE FROM ' . $this->table_users . ' WHERE user_id >= ?';
            $stmt = $this->db->prepare($query, array('integer'), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $row = intval($total_rows / 2);
            $stmt->bindParam(0, $row);
            for ($row = $total_rows; $total_rows; $total_rows = $row) {
                $row = intval($total_rows / 2);
    
                $result = $stmt->execute();
    
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
    
                $this->assertEquals(($total_rows - $row), $result, 'Deleting rows returned incorrect affected row count');
    
            }
    
            $stmt->free();
        }
    
        /**
         * Testing transaction support - Test ROLLBACK
         *
         * @dataProvider provider
         */
        public function testTransactionsRollback($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('transactions')) {
                $this->markTestSkipped('Transactions not supported');
            }
    
            $data = $this->getSampleData(0);
    
            $this->db->beginTransaction();
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $result = $stmt->execute(array_values($data));
            $this->db->rollback();
            $stmt->free();
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->query($query);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users ' . $result->getUserInfo());
            }
            $this->assertTrue(!$result->valid(), 'Transaction rollback did not revert the row that was inserted');
            $result->free();
        }
    
        /**
         * Testing transaction support - Test COMMIT
         *
         * @dataProvider provider
         */
        public function testTransactionsCommit($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('transactions')) {
                $this->markTestSkipped('Transactions not supported');
            }
    
            $data = $this->getSampleData(1);
    
            $this->db->beginTransaction();
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $result = $stmt->execute(array_values($data));
            $this->db->commit();
            $stmt->free();
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->query($query);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users ' . $result->getUserInfo());
            }
            $this->assertTrue($result->valid(), 'Transaction commit did not make permanent the row that was inserted');
            $result->free();
        }
    
        /**
         * Testing transaction support - Test COMMIT and ROLLBACK
         *
         * @dataProvider provider
         */
        public function testTransactionsBoth($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('transactions')) {
                $this->markTestSkipped('Transactions not supported');
            }
    
            $data = $this->getSampleData(0);
    
            $this->db->beginTransaction();
            $result = $this->db->exec('DELETE FROM ' . $this->table_users);
            if (MDB2::isError($result)) {
                $this->fail('Error deleting from users: ' . $result->getUserInfo());
                $this->db->rollback();
            } else {
                $this->db->commit();
            }
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->query($query);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: ' . $result->getUserInfo());
            }
    
            $this->assertTrue(!$result->valid(), 'Transaction end with implicit commit when re-enabling auto-commit did not make permanent the rows that were deleted');
            $result->free();
        }
    
        /**
         * Testing emulated nested transaction support
         *
         * @dataProvider provider
         */
        public function testNestedTransactions($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('transactions')) {
                $this->markTestSkipped('Transactions not supported');
            }
    
            $data = array(
                1 => $this->getSampleData(1234),
                2 => $this->getSampleData(4321),
            );
    
            $this->db->beginNestedTransaction();
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $result = $stmt->execute(array_values($data[1]));
    
            $this->db->beginNestedTransaction();
    
            $result = $stmt->execute(array_values($data[2]));
            $stmt->free();
    
            $result = $this->db->completeNestedTransaction();
            if (MDB2::isError($result)) {
                $this->fail('Inner transaction was not committed: ' . $result->getUserInfo());
            }
    
            $result = $this->db->completeNestedTransaction();
            if (MDB2::isError($result)) {
                $this->fail('Outer transaction was not committed: ' . $result->getUserInfo());
            }
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->query($query);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: ' . $result->getUserInfo());
            }
            $this->assertTrue($result->valid(), 'Transaction commit did not make permanent the row that was inserted');
            $result->free();
        }
    
        /**
         * Testing savepoints
         *
         * @dataProvider provider
         */
        public function testSavepoint($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('savepoints')) {
                $this->markTestSkipped('SAVEPOINTs not supported');
            }
    
            $savepoint = 'test_savepoint';
    
            $data = array(
                1 => $this->getSampleData(1234),
                2 => $this->getSampleData(4321),
            );
    
            $this->db->beginTransaction();
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $result = $stmt->execute(array_values($data[1]));
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: ' . $result->getUserInfo());
            }
    
            $result = $this->db->beginTransaction($savepoint);
            if (MDB2::isError($result)) {
                $this->fail('Error setting savepoint: ' . $result->getUserInfo());
            }
    
            $result = $stmt->execute(array_values($data[2]));
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: ' . $result->getUserInfo());
            }
            $stmt->free();
    
            $result = $this->db->rollback($savepoint);
            if (MDB2::isError($result)) {
                $this->fail('Error rolling back to savepoint: ' . $result->getUserInfo());
            }
    
            $result = $this->db->commit();
            if (MDB2::isError($result)) {
                $this->fail('Transaction not committed: ' . $result->getUserInfo());
            }
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->queryAll($query);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users' . $result->getUserInfo());
            }
            $rows_inserted = count($result);
            $this->assertEquals(1, $rows_inserted, 'Error during transaction, invalid number of records inserted');
    
            // test release savepoint
            $this->db->beginTransaction();
            $result = $this->db->beginTransaction($savepoint);
            if (MDB2::isError($result)) {
                $this->fail('Error setting savepoint: ' . $result->getUserInfo());
            }
            $result = $this->db->commit($savepoint);
            if (MDB2::isError($result)) {
                $this->fail('Error setting savepoint: ' . $result->getUserInfo());
            }
            $result = $this->db->commit();
            if (MDB2::isError($result)) {
                $this->fail('Transaction not committed: ' . $result->getUserInfo());
            }
        }
    
        /**
         * Testing LOB storage
         *
         * MYSQL NOTE:  If this test fails with native code 1210,
         * "Incorrect arguments to mysqld_stmt_execute" upgrade to MySQL 5.1.57.
         * If that's not an option, set "general_log = 1" in my.cnf.
         *
         * MSSQL NOTE:  If this test fails, use an higher limit in these
         * two php.ini settings: "mssql.textlimit" and "mssql.textsize"
         *
         * @dataProvider provider
         */
        public function testLOBStorage($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('LOBs')) {
                $this->markTestSkipped('LOBs not supported');
            }
    
            $query = 'INSERT INTO ' . $this->table_files . ' (ID, document, picture) VALUES (1, ?, ?)';
            $stmt = $this->db->prepare($query, array('clob', 'blob'), MDB2_PREPARE_MANIP, array('document', 'picture'));
            if (MDB2::isError($stmt)) {
                $this->fail('Failed prepared statement to insert LOB values: '.$stmt->getUserInfo());
            }
    
            $character_lob = '';
            $binary_lob    = '';
    
            for ($i = 0; $i < 1000; $i++) {
                for ($code = 32; $code <= 127; $code++) {
                    $character_lob.= chr($code);
                }
                for ($code = 0; $code <= 255; $code++) {
                    $binary_lob.= chr($code);
                }
            }
    
            $stmt->bindValue(0, $character_lob);
            $stmt->bindValue(1, $binary_lob);
    
            $result = $stmt->execute();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: '.$result->getUserInfo());
            }
    
            $stmt->free();
    
            $result = $this->db->query('SELECT document, picture FROM ' . $this->table_files . ' WHERE id = 1', array('clob', 'blob'));
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from files' . $result->getUserInfo());
            }
    
            $this->assertTrue($result->valid(), 'The query result seem to have reached the end of result too soon.');
    
            $row = $result->fetchRow();
            $clob = $row[0];
            if (!MDB2::isError($clob) && is_resource($clob)) {
                $value = '';
                while (!feof($clob)) {
                    $data = fread($clob, 8192);
                    $this->assertTrue(strlen($data) >= 0, 'Could not read CLOB');
                    $value.= $data;
                }
                $this->db->datatype->destroyLOB($clob);
                $this->assertEquals($character_lob, $value, 'Retrieved character LOB value is different from what was stored');
            } else {
                $this->fail('Error retrieving CLOB result');
            }
    
            $blob = $row[1];
            if (!MDB2::isError($blob) && is_resource($blob)) {
                $value = '';
                while (!feof($blob)) {
                    $data = fread($blob, 8192);
                    $this->assertTrue(strlen($data) >= 0, 'Could not read BLOB');
                    $value.= $data;
                }
    
                $this->db->datatype->destroyLOB($blob);
                $this->assertEquals($binary_lob, $value, 'Retrieved binary LOB value is different from what was stored');
            } else {
                $this->fail('Error retrieving BLOB result');
            }
            $result->free();
        }
    
        /**
         * Test LOB reading of multiple records both buffered and unbuffered. See bug #8793 for why this must be tested.
         *
         * @dataProvider provider
         */
        public function testLOBRead($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('LOBs')) {
                $this->markTestSkipped('LOBs not supported');
            }
    
            for ($i = 20; $i < 30; ++$i) {
                $query = 'INSERT INTO ' . $this->table_files . ' (ID, document, picture) VALUES (?, ?, ?)';
                $stmt = $this->db->prepare($query, array('integer', 'clob', 'blob'), MDB2_PREPARE_MANIP, array(1 => 'document', 2 => 'picture'));
                if (MDB2::isError($stmt)) {
                    $this->fail('Error preparing query: ' . $stmt->getUserInfo());
                }
    
                $character_lob = $binary_lob = $i;
                $stmt->bindValue(1, $character_lob);
                $stmt->bindValue(2, $binary_lob);
    
                $result = $stmt->execute(array($i));
    
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: '.$result->getUserInfo());
                }
                $stmt->free();
            }
    
            $oldBuffered = $this->db->getOption('result_buffering');
            foreach (array(true, false) as $buffered) {
                $this->db->setOption('result_buffering', $buffered);
                $msgPost = ' with result_buffering = '.($buffered ? 'true' : 'false');
                $result = $this->db->query('SELECT id, document, picture FROM ' . $this->table_files . ' WHERE id >= 20 AND id <= 30 ORDER BY id ASC', array('integer', 'clob', 'blob'));
                if (MDB2::isError($result)) {
                    $this->fail('Error selecting from files ' . $msgPost . ': ' . $result->getMessage());
                } else {
                    if ($buffered) {
                        $this->assertTrue($result->valid(), 'The query result seem to have reached the end of result too soon'.$msgPost);
                        $this->assertEquals('mdb2_bufferedresult_', strtolower(substr(get_class($result), 0, 20)), 'Error: not a buffered result');
                    } else {
                        $this->assertEquals('mdb2_result_', strtolower(substr(get_class($result), 0, 12)), 'Error: an unbuffered result was expected');
                    }
                    for ($i = 1; $i <= ($buffered ? 2 : 1); ++$i) {
                        $result->seek(0);
                        while ($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC)) {
                            foreach (array('document' => 'clob', 'picture' => 'blob') as $field => $type) {
                                $lob = $row[$field];
                                if (is_object($lob) && is_a($lob, 'oci-lob')) {
                                    $lob = $lob->load();
                                } elseif (is_resource($lob)) {
                                    $lob = fread($lob, 1000);
                                }
                                $this->assertEquals($lob, $row['id'], 'LOB ('.$type.') field ('.$field.') not equal to expected value ('.$row['id'].')'.$msgPost.' on run-through '.$i);
                            }
                        }
                    }
                    $result->free();
                }
            }
            $this->db->setOption('result_buffering', $oldBuffered);
        }
    
        /**
         * Test for lob storage from and to files
         *
         * @dataProvider provider
         */
        public function testLOBFiles($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('LOBs')) {
                $this->markTestSkipped('LOBs not supported');
            }
    
            // Create character data file.
            $character_data_file = tempnam(sys_get_temp_dir(), 'mdb2_clob_data_');
            $file = fopen($character_data_file, 'w');
            $this->assertTrue(((bool)$file), 'Error creating clob file to read from');
            $character_data = '';
            for ($i = 0; $i < 1000; $i++) {
                for ($code = 32; $code <= 127; $code++) {
                    $character_data.= chr($code);
                }
            }
            if (fwrite($file, $character_data, strlen($character_data)) != strlen($character_data)) {
                @fclose($file);
                @unlink($character_data_file);
                $this->fail('Error writing to clob file: ' . $character_data_file);
            }
            fclose($file);
    
            // Create binary data file.
            $binary_data_file = tempnam(sys_get_temp_dir(), 'mdb2_blob_data_');
            $file = fopen($binary_data_file, 'wb');
            $this->assertTrue(((bool)$file), 'Error creating blob file to read from');
            $binary_data = '';
            for ($i = 0; $i < 1000; $i++) {
                for ($code = 0; $code <= 255; $code++) {
                    $binary_data.= chr($code);
                }
            }
            if (fwrite($file, $binary_data, strlen($binary_data)) != strlen($binary_data)) {
                @fclose($file);
                @unlink($binary_data_file);
                $this->fail('Error writing to blob file: ' . $binary_data_file);
            }
            fclose($file);
    
    
            // Insert data files into database.
    
            $this->db->setOption('lob_allow_url_include', true);
    
            $query = 'INSERT INTO ' . $this->table_files . ' (ID, document, picture) VALUES (1, :document, :picture)';
            $stmt = $this->db->prepare($query, array('document' => 'clob', 'picture' => 'blob'), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $character_data_file_tmp = 'file://'.$character_data_file;
            $stmt->bindParam('document', $character_data_file_tmp);
            $binary_data_file_tmp = 'file://'.$binary_data_file;
            $stmt->bindParam('picture', $binary_data_file_tmp);
    
            $result = $stmt->execute();
            if (MDB2::isError($result)) {
                @$stmt->free();
                $this->fail('Error executing prepared query - inserting LOB from files: ' . $result->getUserInfo());
            }
    
            $stmt->free();
            @unlink($character_data_file);
            @unlink($binary_data_file);
    
    
            // Query the newly created record.
            $result = $this->db->query('SELECT document, picture FROM ' . $this->table_files . ' WHERE id = 1', array('clob', 'blob'));
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from files: ' . $result->getUserInfo());
            }
            $this->assertTrue($result->valid(), 'The query result seem to have reached the end of result too soon.');
            $row = $result->fetchRow();
    
            $clob = $row[0];
            if (MDB2::isError($clob) || !is_resource($clob)) {
                $result->free();
                $this->fail('Error reading CLOB from database.');
            }
    
            $blob = $row[1];
            if (MDB2::isError($blob) || !is_resource($blob)) {
                $result->free();
                $this->fail('Error reading BLOB from database.');
            }
    
    
            // Write CLOB to file and verify file contents.
            $res = $this->db->datatype->writeLOBToFile($clob, $character_data_file);
            $this->db->datatype->destroyLOB($clob);
            if (MDB2::isError($res)) {
                @unlink($character_data_file);
                $this->fail('Error writing CLOB to file.');
            }
    
            $value = file_get_contents($character_data_file);
            @unlink($character_data_file);
            if (false === $value) {
                $this->fail("Error opening CLOB file: $character_data_file");
            }
            $this->assertEquals($character_data, $value, "retrieved character LOB value is different from what was stored");
    
    
            // Write BLOB to file and verify file contents.
            $res = $this->db->datatype->writeLOBToFile($blob, $binary_data_file);
            $this->db->datatype->destroyLOB($blob);
            if (MDB2::isError($res)) {
                @unlink($binary_data_file);
                $this->fail('Error writing BLOB to file.');
            }
    
            $value = file_get_contents($binary_data_file);
            @unlink($binary_data_file);
            if (false === $value) {
                $this->fail("Error opening BLOB file: $binary_data_file");
            }
            $this->assertEquals($binary_data, $value, "retrieved binary LOB value is different from what was stored");
    
    
            // Clean up.
            $result->free();
        }
    
        /**
         * Test for lob storage from and to files
         *
         * @dataProvider provider
         */
        public function testQuoteLOBFilesNoUrlInclude($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('LOBs')) {
                $this->markTestSkipped('LOBs not supported');
            }
    
            $this->db->setOption('lob_allow_url_include', false);
    
            $character_data_file = tempnam(sys_get_temp_dir(), 'mdb2_clob_data_');
            $character_data_file_tmp = 'file://'.$character_data_file;
            $file = fopen($character_data_file, 'w');
            $this->assertTrue(((bool)$file), 'Error creating clob file to read from');
            $character_data = '';
            for ($code = 65; $code <= 80; $code++) {
                $character_data.= chr($code);
            }
            if (fwrite($file, $character_data, strlen($character_data)) != strlen($character_data)) {
                @fclose($file);
                @unlink($character_data_file);
                $this->fail('Error writing to clob file: ' . $character_data_file);
            }
            fclose($file);
    
            $expected = ($this->dsn['phptype'] == 'oci8') ? 'EMPTY_CLOB()' : "'".$character_data_file_tmp."'";
            $quoted = $this->db->quote($character_data_file_tmp,  'clob');
            if ($expected != $quoted) {
                // Wipe out file before test fails and rest of method gets skipped.
                @unlink($character_data_file);
            }
            $this->assertEquals($expected, $quoted, 'clob data did not match');
    
            $expected = ($this->dsn['phptype'] == 'oci8') ? 'EMPTY_BLOB()' : "'".$character_data_file_tmp."'";
            $quoted = $this->db->quote($character_data_file_tmp,  'blob');
            if ($expected != $quoted) {
                // Wipe out file before test fails and rest of method gets skipped.
                @unlink($character_data_file);
            }
            $this->assertEquals($expected, $quoted, 'blob data did not match');
    
            @unlink($character_data_file);
        }
    
        /**
         * Test for lob storage from and to files
         *
         * @dataProvider provider
         */
        public function testQuoteLOBFilesUrlInclude($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('LOBs')) {
                $this->markTestSkipped('LOBs not supported');
            }
    
            $this->db->setOption('lob_allow_url_include', true);
    
            $character_data_file = tempnam(sys_get_temp_dir(), 'mdb2_clob_data_');
            $character_data_file_tmp = 'file://'.$character_data_file;
            $file = fopen($character_data_file, 'w');
            $this->assertTrue(((bool)$file), 'Error creating clob file to read from');
            $character_data = '';
            for ($code = 65; $code <= 80; $code++) {
                $character_data.= chr($code);
            }
            if (fwrite($file, $character_data, strlen($character_data)) != strlen($character_data)) {
                @fclose($file);
                @unlink($character_data_file);
                $this->fail('Error writing to clob file: ' . $character_data_file);
            }
            fclose($file);
    
            $expected = ($this->dsn['phptype'] == 'oci8') ? 'EMPTY_CLOB()' : "'".$character_data."'";
            $quoted = $this->db->quote($character_data_file_tmp,  'clob');
            $this->assertEquals($expected, $quoted);
    
            switch ($this->dsn['phptype']) {
                case 'oci8':
                    $expected = 'EMPTY_BLOB()';
                    break;
                case 'sqlsrv':
                    $expected = "'0x".bin2hex($character_data)."'";
                    break;
                default:
                    $expected = "'".$character_data."'";
                    break;
            }
            $quoted = $this->db->quote($character_data_file_tmp,  'blob');
            $this->assertEquals($expected, $quoted);
    
            @unlink($character_data_file);
        }
    
        /**
         * Test handling of lob nulls
         *
         * @dataProvider provider
         */
        public function testLOBNulls($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('LOBs')) {
                $this->markTestSkipped('LOBs not supported');
            }
    
            $query = 'INSERT INTO ' . $this->table_files . ' (ID, document, picture) VALUES (1, :document, :picture)';
            $stmt = $this->db->prepare($query, array('document' => 'clob', 'picture' => 'blob'), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $null = null;
            $stmt->bindParam('document', $null);
            $stmt->bindParam('picture', $null);
    
            $result = $stmt->execute();
            if (MDB2::isError($result)) {
                @$stmt->free();
                $this->fail('Error executing prepared query - inserting NULL lobs: ' . $result->getUserInfo());
            }
            $stmt->free();
    
            $result = $this->db->query('SELECT document, picture FROM ' . $this->table_files, array('clob', 'blob'));
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from files: ' . $result->getUserInfo());
            }
    
            $this->assertTrue($result->valid(), 'The query result seem to have reached the end of result too soon.');
    
            $row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
            $this->assertTrue(is_null($row['document']), 'A query result large object column document is not NULL unlike what was expected');
            $this->assertTrue(is_null($row['picture']), 'A query result large object column picture is not NULL unlike what was expected');
    
            $result->free();
        }
    
        /**
         * @dataProvider provider
         */
        public function testLOBUpdate($ci) {
            $this->manualSetUp($ci);
    
            if (!$this->supported('LOBs')) {
                $this->markTestSkipped('LOBs not supported');
            }
    
            $query = 'INSERT INTO ' . $this->table_files . ' (ID, document, picture) VALUES (1, ?, ?)';
            $stmt = $this->db->prepare($query, array('clob', 'blob'), MDB2_PREPARE_MANIP, array('document', 'picture'));
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $character_lob = '';
            $binary_lob = '';
    
            for ($i = 0; $i < 1000; $i++) {
                for ($code = 32; $code <= 127; ++$code) {
                    $character_lob .= chr($code);
                }
                for ($code = 0; $code <= 255; ++$code) {
                    $binary_lob .= chr($code);
                }
            }
    
            $stmt->bindValue(0, $character_lob);
            $stmt->bindValue(1, $binary_lob);
    
            $result = $stmt->execute();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: '.$result->getUserInfo());
            }
    
            $stmt->free();
    
            $query = 'UPDATE ' . $this->table_files . ' SET document = ?, picture = ? WHERE ID = 1';
            $stmt = $this->db->prepare($query, array('clob', 'blob'), MDB2_PREPARE_MANIP, array('document', 'picture'));
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $character_lob = '';
            $binary_lob = '';
    
            for ($i = 0; $i < 999; $i++) {
                for ($code = 127; $code >= 32; --$code) {
                    $character_lob .= chr($code);
                }
                for ($code = 255; $code >= 0; --$code) {
                    $binary_lob .= chr($code);
                }
            }
    
            $stmt->bindValue(0, $character_lob);
            $stmt->bindValue(1, $binary_lob);
    
            $result = $stmt->execute();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: '.$result->getUserInfo());
            }
    
            $stmt->free();
    
            $result = $this->db->query('SELECT document, picture FROM ' . $this->table_files . ' WHERE id = 1', array('clob', 'blob'));
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from files: ' . $result->getUserInfo());
            }
    
            $this->assertTrue($result->valid(), 'The query result seem to have reached the end of result too soon.');
    
            $row = $result->fetchRow();
            $clob = $row[0];
            if (!MDB2::isError($clob) && is_resource($clob)) {
                $value = '';
                while (!feof($clob)) {
                    $data = fread($clob, 8192);
                    $this->assertTrue(strlen($data) >= 0, 'Could not read CLOB');
                    $value.= $data;
                }
                $this->db->datatype->destroyLOB($clob);
                $this->assertEquals($character_lob, $value, 'Retrieved character LOB value is different from what was stored');
            } else {
                $this->fail('Error retrieving CLOB result');
            }
    
            $blob = $row[1];
            if (!MDB2::isError($blob) && is_resource($blob)) {
                $value = '';
                while (!feof($blob)) {
                    $data = fread($blob, 8192);
                    $this->assertTrue(strlen($data) >= 0, 'Could not read BLOB');
                    $value.= $data;
                }
    
                $this->db->datatype->destroyLOB($blob);
                $this->assertEquals($binary_lob, $value, 'Retrieved binary LOB value is different from what was stored');
            } else {
                $this->fail('Error retrieving BLOB result');
            }
            $result->free();
        }
    
        /**
         * Test retrieval of result metadata
         *
         * This tests the result metadata by executing a prepared query and
         * select the data, and checking the result contains the correct
         * number of columns and that the column names are in the correct order
         *
         * @dataProvider provider
         */
        public function testConvertEmpty2Null($ci) {
            $this->manualSetUp($ci);
    
    #$this->db->setOption('portability', MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_EMPTY_TO_NULL);
    
            $data = $this->getSampleData(1234);
            $data['user_password'] = '';
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $result = $stmt->execute(array_values($data));
            $stmt->free();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: ' . $result->getUserInfo());
            }
    
            $row = $this->db->queryRow('SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users . ' WHERE user_password IS NULL', $this->fields, MDB2_FETCHMODE_ORDERED);
    
            if (MDB2::isError($row)) {
                $this->fail('Error selecting from users: ' . $result->getUserInfo());
            }
    
            $expected = count($this->fields);
            $actual   = count($row);
            $this->assertEquals($expected, $actual, "The query result returned a number of columns ({$actual}) unlike {$expected} as expected");
        }
    
        /** @dataProvider provider */
        public function testPortabilityOptions($ci) {
            $this->manualSetUp($ci);
    
            // MDB2_PORTABILITY_DELETE_COUNT
            $data = array();
            $total_rows = 5;
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            for ($row = 0; $row < $total_rows; $row++) {
                $data[$row] = $this->getSampleData($row);
                $result = $stmt->execute(array_values($data[$row]));
                if (MDB2::isError($result)) {
                    $this->fail('Error executing prepared query: ' . $result->getUserInfo());
                }
            }
            $stmt->free();
    
            $this->db->setOption('portability', MDB2_PORTABILITY_NONE | MDB2_PORTABILITY_DELETE_COUNT);
            $affected_rows = $this->db->exec('DELETE FROM ' . $this->table_users);
            if (MDB2::isError($affected_rows)) {
                $this->fail('Error executing query: '.$affected_rows->getMessage());
            }
            $this->assertEquals($total_rows, $affected_rows, 'MDB2_PORTABILITY_DELETE_COUNT not working');
    
            // MDB2_PORTABILITY_FIX_CASE
            $fields = array_keys($this->fields);
            $this->db->setOption('portability', MDB2_PORTABILITY_NONE | MDB2_PORTABILITY_FIX_CASE);
            $this->db->setOption('field_case', CASE_UPPER);
    
            $data = $this->getSampleData(1234);
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $result = $stmt->execute(array_values($data));
            $stmt->free();
    
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->queryRow($query, $this->fields, MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: ' . $result->getUserInfo());
            }
            $field = reset($fields);
            foreach (array_keys($result) as $fieldname) {
                $this->assertEquals(strtoupper($field), $fieldname, 'MDB2_PORTABILITY_FIX_CASE CASE_UPPER not working');
                $field = next($fields);
            }
    
            $this->db->setOption('field_case', CASE_LOWER);
            $query = 'SELECT ' . implode(', ', array_keys($this->fields)) . ' FROM ' . $this->table_users;
            $result = $this->db->queryRow($query, $this->fields, MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: ' . $result->getUserInfo());
            }
            $field = reset($fields);
            foreach (array_keys($result) as $fieldname) {
                $this->assertEquals(strtolower($field), $fieldname, 'MDB2_PORTABILITY_FIX_CASE CASE_LOWER not working');
                $field = next($fields);
            }
    
            // MDB2_PORTABILITY_RTRIM
            $this->db->setOption('portability', MDB2_PORTABILITY_NONE | MDB2_PORTABILITY_RTRIM);
            $value = 'rtrim   ';
            $query = 'INSERT INTO ' . $this->table_users . ' (user_id, user_password) VALUES (1, ' . $this->db->quote($value, 'text') .')';
            $res = $this->db->exec($query);
            if (MDB2::isError($res)) {
                $this->fail('Error executing query: '.$res->getMessage());
            }
            $query = 'SELECT user_password FROM ' . $this->table_users . ' WHERE user_id = 1';
            $result = $this->db->queryOne($query, array('text'));
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from users: ' . $result->getUserInfo());
            }
            $this->assertEquals(rtrim($value), $result, '"MDB2_PORTABILITY_RTRIM = on" not working');
    
            if (!$this->supported('LOBs')) {
                return;
            }
    
            $query = 'INSERT INTO ' . $this->table_files . ' (ID, document, picture) VALUES (1, ?, ?)';
            $stmt = $this->db->prepare($query, array('clob', 'blob'), MDB2_PREPARE_MANIP, array('document', 'picture'));
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $character_lob = '';
            $binary_lob = '';
    
            for ($i = 0; $i < 999; $i++) {
                for ($code = 127; $code >= 32; --$code) {
                    $character_lob .= chr($code);
                }
                for ($code = 255; $code >= 0; --$code) {
                    $binary_lob .= chr($code);
                }
            }
    
            $stmt->bindValue(0, $character_lob);
            $stmt->bindValue(1, $binary_lob);
    
            $result = $stmt->execute();
    
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: '.$result->getUserInfo());
            }
    
            $stmt->free();
    
            $result = $this->db->query('SELECT document, picture FROM ' . $this->table_files . ' WHERE id = 1', array('clob', 'blob'));
            if (MDB2::isError($result)) {
                $this->fail('Error selecting from files: ' . $result->getUserInfo());
            }
    
            $this->assertTrue($result->valid(), 'The query result seem to have reached the end of result too soon.');
    
            $row = $result->fetchRow();
            $clob = $row[0];
            if (!MDB2::isError($clob) && is_resource($clob)) {
                $value = '';
                while (!feof($clob)) {
                    $data = fread($clob, 8192);
                    $this->assertTrue(strlen($data) >= 0, 'Could not read CLOB');
                    $value.= $data;
                }
                $this->db->datatype->destroyLOB($clob);
                $this->assertEquals($character_lob, $value, '"MDB2_PORTABILITY_RTRIM = on" Retrieved character LOB value is different from what was stored');
            } else {
                $this->fail('Error retrieving CLOB result');
            }
    
            $blob = $row[1];
            if (!MDB2::isError($blob) && is_resource($blob)) {
                $value = '';
                while (!feof($blob)) {
                    $data = fread($blob, 8192);
                    $this->assertTrue(strlen($data) >= 0, 'Could not read BLOB');
                    $value.= $data;
                }
    
                $this->db->datatype->destroyLOB($blob);
                $this->assertEquals($binary_lob, $value, '"MDB2_PORTABILITY_RTRIM = on" Retrieved binary LOB value is different from what was stored');
            } else {
                $this->fail('Error retrieving BLOB result');
            }
            $result->free();
        }
    
        /**
         * Test getAsKeyword()
         *
         * @dataProvider provider
         */
        public function testgetAsKeyword($ci) {
            $this->manualSetUp($ci);
    
            $query = 'INSERT INTO ' . $this->table_users . ' (' . implode(', ', array_keys($this->fields)) . ') VALUES ('.implode(', ', array_fill(0, count($this->fields), '?')).')';
            $stmt = $this->db->prepare($query, array_values($this->fields), MDB2_PREPARE_MANIP);
            if (MDB2::isError($stmt)) {
                $this->fail('Error preparing query: ' . $stmt->getUserInfo());
            }
    
            $data = $this->getSampleData(1);
            $result = $stmt->execute(array_values($data));
            if (MDB2::isError($result)) {
                $this->fail('Error executing prepared query: ' . $result->getUserInfo());
            }
            $stmt->free();
    
            $query = 'SELECT user_id'.$this->db->getAsKeyword().'foo FROM ' . $this->table_users;
            $result = $this->db->queryRow($query, array('integer'), MDB2_FETCHMODE_ASSOC);
            if (MDB2::isError($result)) {
                $this->fail('Error getting alias column: '. $result->getMessage());
            } else {
                $this->assertTrue((array_key_exists('foo', $result)), 'Error: could not alias "user_id" with "foo" : '.var_export($result, true));
            }
        }
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/autoload.inc000066400000000000000000000041101213666021100203700ustar00rootroot00000000000000
     */
    
    $dirs = array();
    
    // Determine this directory.
    $dirs['tests'] = realpath(__DIR__);
    
    // Determine path to the MDB2 classes to be tested.
    if ('@php_dir@' == '@'.'php_dir'.'@') {
        // This package hasn't been installed, use parent of this dir.
        $dirs['mdb2'] = realpath((dirname(__DIR__)));
    } else {
        $dirs['mdb2'] = '@php_dir@';
    }
    /**
     * Path to the MDB2 files we are testing
     */
    define('MDB2_TEST_MDB2_PATH', $dirs['mdb2']);
    
    // Determine if a current version of PHPUnit is installed.
    try {
        $fi = new SplFileObject('PHPUnit/Autoload.php', 'r', true);
        unset($fi);
    } catch (Exception $e) {
        try {
            $fi = new SplFileObject('PHPUnit/Framework.php', 'r', true);
            die("skip Run 'pear upgrade PHPUnit' then retry.\n");
        } catch (Exception $e) {
            die("skip Run 'pear install pear.phpunit.de/PHPUnit' then retry.\n");
        }
    }
    
    // Determine if and where MDB_Schema is installed.
    try {
        $fi = new SplFileObject('MDB2/Schema.php', 'r', true);
        $dirs['mdb2_schema'] = dirname(dirname($fi->getRealPath()));
        unset($fi);
    } catch (Exception $e) {
        die("skip Run 'pear install MDB2_Schema-beta' then retry.\n");
    }
    
    // Set the include path.
    $dirs = array_unique($dirs);
    set_include_path(implode(PATH_SEPARATOR, $dirs)
        . PATH_SEPARATOR . get_include_path());
    
    /**
     * Automatically includes files for new classes
     */
    function mdb2_test_autoload($class) {
    	$class_file = str_replace('_', '/', $class) . '.php';
        try {
            $fi = new SplFileObject($class_file, 'r', true);
            require_once $fi->getPathname();
            unset($fi);
        } catch (Exception $e) {
            // Fall through, perhaps another auto loader will pick it up.
        }
    }
    spl_autoload_register('mdb2_test_autoload');
    
    /**
     * Line up PHPUnit's autoloader.
     */
    require_once 'PHPUnit/Autoload.php';
    
    /**
     * Gather our functions.
     */
    require_once 'functions.inc';
    
    /**
     * Gather our database connection information.
     */
    require_once 'dsn.inc';
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/dsn.inc000066400000000000000000000225051213666021100173540ustar00rootroot00000000000000_TEST_* environment
     * variables set and have their corresponding extensions loaded
     *
     * _TEST_USER, _MYSQL_TEST_PASSWD and _TEST_DB are required,
     * except for SQLite.
     *
     * SQLite runs by default as long as the extension is loaded.  The tests use
     * :memory: as the database unless the SQLITE_TEST_DB environment variable is
     * defined.
     *
     *  can be one or more of the following:
     *   + FBSQL
     *   + IBASE
     *   + MSSQL
     *   + MYSQL (for mysql and mysqli extensions)
     *   + OCI8
     *   + PGSQL
     *   + SQLITE
     *   + SQLSRV
     *
     * Examine this file to see exactly which settings are available for
     * the DBMS's you are interested in testing.
     *
     * In the event you do NOT want a particular driver tested but you have that
     * extension installed in PHP and have the requisite environment variables set,
     * that driver's tests can be disabled by UN-commenting the corresponding
     * entry in the $skip array at the top of this script.
     *
     * The resulting DSN's are passed to serialize() and stored in the
     * MDB2_TEST_SERIALIZED_DSNS constant.
     *
     * @package MDB2
     * @category Database
     * @author Daniel Convissor 
     */
    
    
    // See description in the docblock, above.
    $skip = array(
    //    'fbsql',
    //    'ibase',
    //    'mssql',
    //    'mysql',
    //    'mysqli',
    //    'oci8',
    //    'pgsql',
    //    'sqlite',
    //    'sqlsrv',
    );
    
    
    $dsns = array();
    
    if (!empty($_ENV['FBSQL_TEST_USER']) && !in_array('fbsql', $skip)
        && is_driver_available('fbsql') && extension_loaded('fbsql'))
    {
        $dsns['fbsql'] = array(
            'dsn' => array(
                'phptype' => 'fbsql',
                'username' => $_ENV['FBSQL_TEST_USER'],
                'password' => $_ENV['FBSQL_TEST_PASSWD'],
                'database' => $_ENV['FBSQL_TEST_DB'],
    
                'hostspec' => empty($_ENV['FBSQL_TEST_HOST'])
                        ? null : $_ENV['FBSQL_TEST_HOST'],
            ),
            'options' => array(
            ),
        );
    }
    
    if (!empty($_ENV['IBASE_TEST_USER']) && !in_array('ibase', $skip)
        && is_driver_available('ibase') && extension_loaded('interbase'))
    {
        $dsns['ibase'] = array(
            'dsn' => array(
                'phptype' => 'ibase',
                'username' => $_ENV['IBASE_TEST_USER'],
                'password' => $_ENV['IBASE_TEST_PASSWD'],
                'database' => $_ENV['IBASE_TEST_DB'],
    
                'hostspec' => empty($_ENV['IBASE_TEST_HOST'])
                        ? null : $_ENV['IBASE_TEST_HOST'],
    
                'buffers' => empty($_ENV['IBASE_TEST_BUFFERS'])
                        ? null : $_ENV['IBASE_TEST_BUFFERS'],
    
                'dialect' => empty($_ENV['IBASE_TEST_DIALECT'])
                        ? null : $_ENV['IBASE_TEST_DIALECT'],
    
                'role' => empty($_ENV['IBASE_TEST_ROLE'])
                        ? null : $_ENV['IBASE_TEST_ROLE'],
    
                'dbsyntax' => empty($_ENV['IBASE_TEST_SYNTAX'])
                        ? null : $_ENV['IBASE_TEST_SYNTAX'],
            ),
            'options' => array(
            ),
        );
    }
    
    if (!empty($_ENV['MSSQL_TEST_USER']) && !in_array('mssql', $skip)
        && is_driver_available('mssql') && function_exists('mssql_connect'))
    {
        $dsns['mssql'] = array(
            'dsn' => array(
                'phptype' => 'mssql',
                'username' => $_ENV['MSSQL_TEST_USER'],
                'password' => $_ENV['MSSQL_TEST_PASSWD'],
                'database' => $_ENV['MSSQL_TEST_DB'],
    
                'hostspec' => empty($_ENV['MSSQL_TEST_HOST'])
                        ? null : $_ENV['MSSQL_TEST_HOST'],
    
                'port' => empty($_ENV['MSSQL_TEST_PORT'])
                        ? null : $_ENV['MSSQL_TEST_PORT'],
            ),
            'options' => array(
            ),
        );
    }
    
    if (!empty($_ENV['MYSQL_TEST_USER']) && !in_array('mysql', $skip)
        && is_driver_available('mysql') && extension_loaded('mysql'))
    {
        $dsns['mysql'] = array(
            'dsn' => array(
                'phptype' => 'mysql',
                'username' => $_ENV['MYSQL_TEST_USER'],
                'password' => $_ENV['MYSQL_TEST_PASSWD'],
                'database' => $_ENV['MYSQL_TEST_DB'],
    
                'hostspec' => empty($_ENV['MYSQL_TEST_HOST'])
                        ? null : $_ENV['MYSQL_TEST_HOST'],
    
                'port' => empty($_ENV['MYSQL_TEST_PORT'])
                        ? null : $_ENV['MYSQL_TEST_PORT'],
    
                'socket' => empty($_ENV['MYSQL_TEST_SOCKET'])
                        ? null : $_ENV['MYSQL_TEST_SOCKET'],
    
                // Hack for MDB2's silly connect method logic.
                'protocol' => empty($_ENV['MYSQL_TEST_SOCKET'])
                        ? null : 'unix',
            ),
            'options' => array(
                'use_transactions' => true
            ),
        );
    }
    
    if (!empty($_ENV['MYSQL_TEST_USER']) && !in_array('mysqli', $skip)
        && is_driver_available('mysqli') && extension_loaded('mysqli'))
    {
        $dsns['mysqli'] = array(
            'dsn' => array(
                'phptype' => 'mysqli',
                'username' => $_ENV['MYSQL_TEST_USER'],
                'password' => $_ENV['MYSQL_TEST_PASSWD'],
                'database' => $_ENV['MYSQL_TEST_DB'],
    
                'hostspec' => empty($_ENV['MYSQL_TEST_HOST'])
                        ? null : $_ENV['MYSQL_TEST_HOST'],
    
                'port' => empty($_ENV['MYSQL_TEST_PORT'])
                        ? null : $_ENV['MYSQL_TEST_PORT'],
    
                'socket' => empty($_ENV['MYSQL_TEST_SOCKET'])
                        ? null : $_ENV['MYSQL_TEST_SOCKET'],
            ),
            'options' => array(
                'use_transactions' => true
            ),
        );
    }
    
    if (!empty($_ENV['OCI8_TEST_USER']) && !in_array('oci8', $skip)
        && is_driver_available('oci8') && extension_loaded('oci8'))
    {
        $dsns['oci8'] = array(
            'dsn' => array(
                'phptype' => 'oci8',
                'username' => empty($_ENV['OCI8_TEST_USER'])
                        ? null : $_ENV['OCI8_TEST_USER'],
    
                'password' => empty($_ENV['OCI8_TEST_PASSWD'])
                        ? null : $_ENV['OCI8_TEST_PASSWD'],
    
                'database' => empty($_ENV['OCI8_TEST_DB'])
                        ? null : $_ENV['OCI8_TEST_DB'],
    
                'hostspec' => empty($_ENV['OCI8_TEST_HOST'])
                        ? null : $_ENV['OCI8_TEST_HOST'],
    
                'port' => empty($_ENV['OCI8_TEST_PORT'])
                        ? null : $_ENV['OCI8_TEST_PORT'],
    
                'service' => empty($_ENV['OCI8_TEST_SERVICE'])
                        ? null : $_ENV['OCI8_TEST_SERVICE'],
            ),
            'options' => array(
            ),
        );
    }
    
    if (!empty($_ENV['PGSQL_TEST_USER']) && !in_array('pgsql', $skip)
        && is_driver_available('pgsql') && extension_loaded('pgsql'))
    {
        $dsns['pgsql'] = array(
            'dsn' => array(
                'phptype' => 'pgsql',
                'username' => $_ENV['PGSQL_TEST_USER'],
                'password' => $_ENV['PGSQL_TEST_PASSWD'],
                'database' => $_ENV['PGSQL_TEST_DB'],
    
                'hostspec' => empty($_ENV['PGSQL_TEST_HOST'])
                        ? null : $_ENV['PGSQL_TEST_HOST'],
    
                'port' => empty($_ENV['PGSQL_TEST_PORT'])
                        ? null : $_ENV['PGSQL_TEST_PORT'],
    
                'socket' => empty($_ENV['PGSQL_TEST_SOCKET'])
                        ? null : $_ENV['PGSQL_TEST_SOCKET'],
    
                'protocol' => empty($_ENV['PGSQL_TEST_PROTOCOL'])
                        ? null : $_ENV['PGSQL_TEST_PROTOCOL'],
    
                'options' => empty($_ENV['PGSQL_TEST_OPTIONS'])
                        ? null : $_ENV['PGSQL_TEST_OPTIONS'],
    
                'tty' => empty($_ENV['PGSQL_TEST_TTY'])
                        ? null : $_ENV['PGSQL_TEST_TTY'],
    
                'connect_timeout' => empty($_ENV['PGSQL_TEST_CONNECT_TIMEOUT'])
                        ? null : $_ENV['PGSQL_TEST_CONNECT_TIMEOUT'],
    
                'sslmode' => empty($_ENV['PGSQL_TEST_SSL_MODE'])
                        ? null : $_ENV['PGSQL_TEST_SSL_MODE'],
    
                'service' => empty($_ENV['PGSQL_TEST_SERVICE'])
                        ? null : $_ENV['PGSQL_TEST_SERVICE'],
            ),
            'options' => array(
            ),
        );
    }
    
    // SQLite is different.  Runs using :memory: table even if variable is not set.
    if (!in_array('sqlite', $skip)
        && is_driver_available('sqlite') && extension_loaded('sqlite'))
    {
        if (empty($_ENV['SQLITE_TEST_DB'])) {
            $dsns['sqlite'] = array(
                'dsn' => array(
                    'phptype' => 'sqlite',
                    'database' => ':memory:',
                ),
                'options' => array(
                ),
            );
        } else {
            $dsns['sqlite'] = array(
                'dsn' => array(
                    'phptype' => 'sqlite',
                    'database' => $_ENV['SQLITE_TEST_DB'],
                ),
                'options' => array(
                ),
            );
        }
    }
    
    if (!empty($_ENV['SQLSRV_TEST_USER']) && !in_array('sqlsrv', $skip)
        && is_driver_available('sqlsrv') && extension_loaded('sqlsrv'))
    {
        $dsns['sqlsrv'] = array(
            'dsn' => array(
                'phptype' => 'sqlsrv',
                'username' => $_ENV['SQLSRV_TEST_USER'],
                'password' => $_ENV['SQLSRV_TEST_PASSWD'],
                'database' => $_ENV['SQLSRV_TEST_DB'],
    
                'hostspec' => empty($_ENV['SQLSRV_TEST_HOST'])
                        ? null : $_ENV['SQLSRV_TEST_HOST'],
    
                'port' => empty($_ENV['SQLSRV_TEST_PORT'])
                        ? null : $_ENV['SQLSRV_TEST_PORT'],
            ),
            'options' => array(
            ),
        );
    }
    
    
    // Just making sure to avoid fatal errors if the file gets included twice.
    if (!defined('MDB2_TEST_SERIALIZED_DSNS')) {
        /**
         * A serialize()'ed array of DSN's for all active databases
         */
        define('MDB2_TEST_SERIALIZED_DSNS', serialize($dsns));
    }
    php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/functions.inc000066400000000000000000000073061213666021100206020ustar00rootroot00000000000000
     */
    
    /**
     * Builds the tables used by the test suite.
     *
     * @param array|MDB2_Driver_Common $ci  either a MDB2_Driver_Common object or
     *                   an associative array with two elements.  The "dsn"
     *                   element must contain an array of DSN information.
     *                   The "options" element must be an array of connection
     *                   options.
     */
    function build_schema($ci) {
        $file = __DIR__ . '/schema.xml';
    
        if (is_object($ci)) {
            if (! $ci instanceof MDB2_Driver_Common) {
                die("Must be a MDB2_Driver_Common object.\n");
            }
            $db = $ci;
            $database = $ci->getDatabase();
            $phptype = $ci->phptype;
            $original_options = $ci->options;
        } else {
            if (!is_array($ci['dsn'])) {
                die('$ci["dsn"] must use the array DSN format.' . "\n");
            }
            $db = $ci['dsn'];
            $database = $db['database'];
            $phptype = $db['phptype'];
    
            if (!array_key_exists('options', $ci)) {
                die('$ci["options"] is missing.' . "\n");
            }
            $original_options = $ci['options'];
        }
    
        $variables = array(
            'name'   => $database,
            'create' => true,
        );
    
        $options = array(
            'log_line_break'   => '
    ', 'idxname_format' => '%s', 'debug' => true, 'quote_identifier' => true, 'force_defaults' => false, 'portability' => false ); $options = array_merge($options, $original_options); $err_base = "TEST SCHEMA BUILD ERROR FOR $phptype: "; $schema = MDB2_Schema::factory($db, $options); if (MDB2::isError($schema)) { die($err_base . $schema->getMessage() . ' ' . $schema->getUserInfo() . "\n"); } $definition = $schema->parseDatabaseDefinitionFile($file, $variables, true, true); if (MDB2::isError($definition)) { die($err_base . $definition->getMessage() . ' - ' . $definition->getUserInfo() . "\n"); } else { $operation = $schema->createDatabase($definition); if (MDB2::isError($operation)) { die($err_base . $operation->getMessage() . ' ' . $operation->getUserInfo() . "\n"); } } } /** * Determines if the desired MDB2_Driver class is available IN THE LOCATION * WE ARE TESTING * * Because there's not much point in testing some other installation. * * @param string $phptype the "phptype" of the driver we're looking for * @return bool * @uses MDB2_TEST_MDB2_PATH to determine the path */ function is_driver_available($phptype) { return file_exists(MDB2_TEST_MDB2_PATH . "/MDB2/Driver/$phptype.php"); } /** * Produces a multi-diemnsional array containing the connection information * for each DBMS to be tested * * The connection information for each DBMS is an associative array with two * elements. The "dsn" element must contain an array of DSN information. * The "options" element must be an array of connection options. * * Used by Standard_Abstract::provider() * * @return array */ function mdb2_test_db_object_provider() { static $dbs; if (!isset($dbs)) { $dsns = unserialize(MDB2_TEST_SERIALIZED_DSNS); $dbs = array(); foreach ($dsns as $driver => $ci) { $dbs[$driver] = array( $ci, ); // Disable messages from other packages while building schema. $prior = error_reporting(E_ALL & ~E_STRICT & ~E_DEPRECATED); build_schema($ci); error_reporting($prior); } } return $dbs; } php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/import.schema.php000066400000000000000000000007671213666021100213650ustar00rootroot00000000000000 */ error_reporting(E_ALL & ~E_STRICT & ~E_DEPRECATED); /** * Establish the test suite's environment. */ require_once __DIR__ . '/autoload.inc'; mdb2_test_db_object_provider(); ?> DONE! php-mdb2-2.5.0b5/MDB2-2.5.0b5/tests/schema.xml000066400000000000000000000037061213666021100200610ustar00rootroot00000000000000 name create ascii mdb2_users user_name text 12 false user_password text 8 true subscribed boolean user_id integer 0 1 1 quota decimal weight float access_date date access_time time approved timestamp users_id_index 1 user_id
    mdb2_files id integer 0 1 document clob picture blob lob_id_index 1 id
    php-mdb2-2.5.0b5/package.xml000066400000000000000000002427511213666021100155040ustar00rootroot00000000000000 MDB2 pear.php.net MDB database abstraction layer PEAR MDB2 is a merge of the PEAR DB and Metabase php database abstraction layers. It provides a common API for all supported RDBMS. The main difference to most other DB abstraction packages is that MDB2 goes much further to ensure portability. MDB2 provides most of its many features optionally that can be used to construct portable SQL statements: * Object-Oriented API * A DSN (data source name) or array format for specifying database servers * Datatype abstraction and on demand datatype conversion * Various optional fetch modes to fix portability issues * Portable error codes * Sequential and non sequential row fetching as well as bulk fetching * Ability to make buffered and unbuffered queries * Ordered array and associative array for the fetched rows * Prepare/execute (bind) named and unnamed placeholder emulation * Sequence/autoincrement emulation * Replace emulation * Limited sub select emulation * Row limit emulation * Transactions/savepoint support * Large Object support * Index/Unique Key/Primary Key support * Pattern matching abstraction * Module framework to load advanced functionality on demand * Ability to read the information schema * RDBMS management methods (creating, dropping, altering) * Reverse engineering schemas from an existing database * SQL function call abstraction * Full integration into the PEAR Framework * PHPDoc API documentation Lukas Kahwe Smith lsmith smith@pooteeweet.org no Lorenzo Alberton quipo l.alberton@quipo.it yes Daniel Convissor danielc danielc@php.net yes Paul Cooper pgc pgc@ucecom.com no David Coallier davidc david@jaws.com.mx yes Ali Fazelzadeh afz afz@dev-code.com yes 2012-10-29 2.5.0b5 2.5.0b5 beta beta BSD License - fixed bug #18494: _compareIntegerDefinition() ignores length change. - fixed bug #16280: compareDefinition() turns NULL defaults into empty strings for NOT NULL columns. - fixed bug #19677: Call to undefined function: MDB2_Driver_mysql::isError() open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), etc.) - add support for database/table/row LOCKs - add support for CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - handle LOBs in buffered result sets (Request #8793) 5.2.0 1.9.1 PEAR pear.php.net 1.3.6 MDB2_Driver_fbsql pear.php.net 0.3.0 MDB2_Driver_ibase pear.php.net 1.5.0b4 MDB2_Driver_mssql pear.php.net 1.5.0b4 MDB2_Driver_mysql pear.php.net 1.5.0b4 MDB2_Driver_mysqli pear.php.net 1.5.0b4 MDB2_Driver_oci8 pear.php.net 1.5.0b4 MDB2_Driver_odbc pear.php.net 0.2.0 MDB2_Driver_pgsql pear.php.net 1.5.0b4 MDB2_Driver_querysim pear.php.net 0.7.0 MDB2_Driver_sqlite pear.php.net 1.5.0b4 MDB2_Driver_sqlsrv pear.php.net 1.5.0b5 2.5.0b5 2.5.0b5 beta beta 2012-10-29 BSD License - fixed bug #18494: _compareIntegerDefinition() ignores length change. - fixed bug #16280: compareDefinition() turns NULL defaults into empty strings for NOT NULL columns. - fixed bug #19677: Call to undefined function: MDB2_Driver_mysql::isError() open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), etc.) - add support for database/table/row LOCKs - add support for CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - handle LOBs in buffered result sets (Request #8793) 2.5.0b4 2.5.0b4 beta beta 2012-10-23 BSD License - MDB2 is now E_STRICT compliant! [danielc] - Fix Bug #9502: Strong typing query result misbehaves [danielc] - Fix Bug #16508: mdb2-2.5.0b1 not working with PHP 5.3.0 [quipo] - Fix Bug #17552: MDB2_Driver_Manager_ibase::listTableConstraints returns list of indices [quipo] - Fix Bug #17890: Improper use of array_search in psgsql.php v1.173 prepare function [quipo] - Fix Bug #18050: Many &quot;Deprecated&quot; [quipo] - Fix Bug #18175: Using MDB2::factory raises fatal error [quipo] - Fix Bug #18203: Type introspection breaks with associative arrays if names are identical [danielc] (patch by Peter Bex) - Fix Bug #18398: non-static functions called statically [danielc] - Fix Bug #18427: Notices appear while debugging [quipo] - Fix Bug #18721: DSN URLs do not support &quot;@&quot; in database names [danielc] - Fix Bug #18826: Crash and security problem with is_a() in combination with value escaping [doconnor] - Fix Bug #18886: Deprecated code generates warnings [astembridge] - Fix Bug #18978: upgrade from alpha2 to beta3 breaks iterator.php [danielc] - Fix Bug #19008: remove error_reporting (for PEAR QA team) [danielc] - Fix Bug #19136: Infinite Recurcsion makes result object unuseable [danielc] - Fix Bug #19148: &quot;undefined variable result&quot; in MDB2_Driver_Common::_wrapQuery() [danielc] - Fix Bug #19191: Have dropSequence() return MDB2_OK on success, as documented [danielc] - Fix Bug #19192: Have createSequence() return MDB2_OK on success, as documented [danielc] - Fix Bug #19193: Have createConstraint() return MDB2_OK on success, as documented [danielc] - Fix Bug #19194: Have dropConstraint() return MDB2_OK on success, as documented [danielc] - Fix Bug #19195: Have createIndex() return MDB2_OK on success, as documented [danielc] - Fix Bug #19196: Have vacuum() return MDB2_OK on success, as documented [danielc] - Fix Bug #19199: Have dropTable() return MDB2_OK on success, as documented [danielc] - Fix Bug #19200: Have alterTable() return MDB2_OK on success, as documented [danielc] - Fix Bug #19201: Have truncateTable() return MDB2_OK on success, as documented [danielc] - Fix Bug #19202: sqlite foreign key violations produce generic MDB2_ERROR [danielc] - Fix Bug #19262: Fetchmode constants WERE bitwise [gauthierm] - Implement Feature #17367: Documentation Sync Drift [quipo] - Implement Feature #18759: User note that is a documentation problem [danielc] - small performance tweaks open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), etc.) - add support for database/table/row LOCKs - add support for CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - handle LOBs in buffered result sets (Request #8793) 2.5.0b3 2.5.0b3 beta beta 2010-08-29 BSD License - fixed bug #15912: MDB2::loadClass can return MDB2_OK without loading class - fixed bug #16020: unable to set params via dsn string in parseDSN() - fixed bug #16508: reusing var name + return by ref [rix0r] - fixed bug #16727: quote(url, 'clob')) fetches URL even if lob_allow_url_include option is false - fixed unreported bug in parseDSN() and oracle's Easy Connect syntax - fixed bug #16973 and #17039: Wrong _skipDelimitedStrings behavior with escaped quotes - fixed bug #16994: incompatible declaration of setTransactionIsolation() in some drivers - dropped PHP 4 support - PHP 5.3 compatibility fixes - performance tweaks (reduced number of internal function calls) - added new sqlsrv driver (native SQL Server driver) [Chris Pucci, Mike Ketcham] - added new ODBC experimental driver [pschellhaas] - Switched test suite to PHPUnit 3.5 open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), etc.) - add support for database/table/row LOCKs - add support for CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - handle LOBs in buffered result sets (Request #8793) 2.5.0b2 2.5.0b2 beta beta 2009-01-14 BSD License - fixed bug #12117: disconnect() does not work as documented - fixed bug #12912: replace() documentation - fixed bug #13811: _skipDelimitedStrings() fails on empty strings - fixed bug #13898: more tolerant check for 'new_link' DSN option - request #13929: added 'default_fk_action_ondelete' and 'default_fk_action_onupdate' options for default FOREIGN KEY constraints actions - fixed bug #14124: _skipDelimitedStrings() and prepare() fail with sql comments inside quoted strings - fixed bug #14179: declaration of MDB2_Driver_Common::raiseError() must be compatible with that of PEAR::raiseError() - fixed doc bug #14290: connect should mention the type of object returned - fixed bug #14831: $ignores in _skipDelimitedStrings() [patch by Aleksander Machniak] open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), etc.) - add support for database/table/row LOCKs - add support for CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - handle LOBs in buffered result sets (Request #8793) 2.5.0b1 2.5.0b1 alpha alpha 2008-03-15 BSD License - request #12731: added truncateTable() in the Manager module - request #12732: added vacuum() in the Manager module for OPTIMIZE/VACUUM TABLE abstraction - request #12800: added alterDatabase() in the Manager module [afz] - fixed bug #12924: correctly handle internal expected errors even with custom error handling - fixed bug #12991: new error code in errorInfo() [afz] - added databaseExists() method [afz] - request #13106: added unixtimestamp() in the Function module - added max_identifiers_length option open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), etc.) - add support for database/table/row LOCKs - add support for CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - handle LOBs in buffered result sets (Request #8793) 2.5.0a2 2.5.0a2 alpha alpha 2007-12-06 BSD License - fixed bug #12358: E_STRICT changes in latest alpha broke PHP4 compatibility - fixed bug #12351: wrong case for function PEAR::isError() in the Reverse module [was: IsError()] - fixed bug #12530: MDB2_Extended::autoPrepare() gives error if $types is null - request #12012: added charset/collation support in createDatabase() for the drivers that support this feature - added bindname_format option (this is the regexp used to recognize named placeholders in prepared statements) open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), etc.) - add support for database/table/row LOCKs - add support for CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - handle LOBs in buffered result sets (Request #8793) 2.5.0a1 2.5.0a1 alpha alpha 2007-10-28 BSD License - fixed bug #10024: Security fix for LOBs. Added an option to turn lob_allow_url_include off by default - fixed bug #11179: prepared statements with named placeholders fail if extra values are provided - request #11297: added support for "schema.table" (or "owner.table") notation in the Reverse module - initial support for FOREIGN KEY and CHECK constraints in the Reverse and Manager modules - fixed bug #11428: propagate quote() errors with invalid data types - added new test cases in the test suite - added LENGTH() function in the Function module - fixed bug #11612: raiseError() must be compatible with PEAR::raiseError() [PHP6] - fixed bug #11790: avoid array_diff() because it has a memory leak in PHP 5.1.x - fixed bug #11906: quoteIdentifier fails for names with dots - fixed bug #11975: Extended::autoExecute() does not work with Oracle when using LOBs (patch by Pieter Meulen, van der) - fixed some E_STRICT errors with PHP5 - fixed bug #12083: createTable() in the Manager module now returns MDB2_OK on success, as documented - fixed bug #12246: wrong check in MDB2::isStatement() (thanks to zaa@zaa.pp.ru) open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), etc.) - add support for database/table/row LOCKs - add support for CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - add charset and collation support to field declaration in all drivers - handle LOBs in buffered result sets (Request #8793) 2.4.1 2.4.1 stable stable 2007-05-03 BSD License - fixed bug #10407: propagate errors in MDB2_Statement_Common::execute() - fixed bug #10452: error in getDeclaration() with custom datatype_map and no datatype_map_callback function - fixed bug #10521: quote($val,'decimal') and quote($val,'float') pass unsafe characters - return length as "precision,scale" for NUMERIC and DECIMAL fields in mapNativeDatatype() - fixed bug #10537: safer check for valid MDB2 connection in singleton() [fornax] - fixed bug #10598: MDB2::singleton() not working in some rare conditions [fornax] - in getTableIndexDefinition() and getTableConstraintDefinition() in the Reverse module, also return the field position in the index/constraint - exec() now returns a reference instead of a copy to prevent memory leaks - request #10787: MDB2_Driver_Common::$phptype and $dbsyntax properties are now public open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), LENGTH() etc.) - add support for database/table/row LOCKs - add support for FOREIGN KEYs and CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - add charset and collation support to field declaration in all drivers - handle LOBs in buffered result sets (Request #8793) 2.4.0 2.4.0 stable stable 2007-03-13 BSD License - propagate errors in getTableFieldDefinition() in the Reverse module - internally use MDB2::classExists() wrapper instead of directly calling class_exists() - fixed bug #9502: query result misbehaves when the number of returned columns is greater than the number of passed types - fixed bug #9748: Table name is not quoted in Extended.php buildManipSQL() - fixed bug #9800: when the php extension for the driver fails to load, the error is not propagated correctly and the script dies - propagate errors in the Datatype module - implemented guid() in the Function module [globally unique identifier] (thanks to mario dot adam at schaeffler dot com) - fixed bug #4854: Oracle Easy Connect syntax only works with array DSN - fixed bug #10105: inTransaction() was returning an incorrect value after a call to disconnect() or __destruct() - implemented a fallback mechanism within getTableIndexDefinition() and getTableConstraintDefinition() in the Reverse module to ignore the 'idxname_format' option and use the index name as provided in case of failure before returning an error - added a 'nativetype_map_callback' option to map native data declarations back to custom data types (thanks to Andrew Hill). - fixed bug #10234 and bug #10233: MDB2_Driver_Datatype_Common::mapNativeDatatype() must ensure that it returns the correct length value, or null - added support for TEMPORARY tables (patch by Andrew Hill) - phpdoc fixes - fixed tests to be compatible with PHP4 - added new tests, including some MDB2 internals tests by Andrew Hill and Monique Szpak open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), LENGTH() etc.) - add support for database/table/row LOCKs - add support for FOREIGN KEYs and CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - add charset and collation support to field declaration in all drivers - handle LOBs in buffered result sets (Request #8793) 2.3.0 2.3.0 stable stable 2006-11-03 BSD License - added charset and collation support to field declaration - added SQL comments and quoted identifier handling inside prepared statement parser - expanded length/scale support for numeric types (Request #7170) - added statement_format option to make it possible to define the name used for native statements if supported - fixed bug when setting MDB2_PREPARE_RESULT in autoExecute() - fixed notice in exec() call in autoExecute() - reversed if logic in MDB2::fileExists checking to be more fool proof with safe_mode restrictions (Bug #8296) - moved CREATE TABLE query generation in a separate method for reuse in the drivers - added testLOBRead to test multiple LOB reads - fixed getColumnNames() optional parameter handling (Bug #8857) - phpdoc fix for fetchCol/fetchAll - added an extra if in the autoExecute method of the Extended driver (Bug #8878) - expanded tableInfo() tests - expanded prepare() tests - fix the incorrect use of currID() for lastInsertID() emulation (Bug #9107) - add test for lastInsertID() - also output php version in test results - added supported 'new_link' - dropped use of track_errors because 5.2.0 causes php_errormsg to not get populated if a custom error handler is set and it was causing issues for safe_mode users anyways - added some error handling into the bindValues()/bindParams() methods (Bug #9133) - bindValue() does not need values by reference - fix issue in execute() when parameter is a scalar integer of 0 (Bug #9158) - implemented stream_stat() method (Bug #9092) - better deal with non scalar arguments in test helper function as 5.2.0 explodes otherwise - extracted _skipDelimitedStrings() method from prepare() - added test for _skipDelimitedStrings() method - migrated to package.xml version 2 open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - expand charset support in schema management and result set handling (Request #4666) - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), LENGTH() etc.) - add support for database/table/row LOCKs - add support for FOREIGN KEYs and CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - explore use of install groups (pear install MDB2#mysql) - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) - add a getTableFieldsDefinitions() method to be used in tableInfo() - drop ILIKE from matchPattern() and instead add a second parameter to handle case sensitivity with arbitrary operators - add charset and collation support to field declaration in all drivers - handle LOBs in buffered result sets (Request #8793) 2.2.2 2.2.2 stable stable 2006-09-03 BSD License - flip positions property array in prepared statement objects to make it possible to optionally use the same named placeholder in multiple places inside a single prepared statement - expanded prepare tests to cover more edge cases - renamed valid_types property to valid_default_values in the Datatype module open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - expand charset support in schema management and result set handling (Request #4666) - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - expand length/scale support for numeric types (Request #7170) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), LENGTH() etc.) - add support for database/table/row LOCKs - add support for FOREIGN KEYs and CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add a package2.xml and explore use of install groups (pear install MDB2#mysql) - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected - handle implicit commits (like for DDL) in any affected driver (mysql, sqlite..) 2.2.1 2.2.1 stable stable 2006-08-21 BSD License - fixed missing code in tableInfo() (Bug #8289) - fixed handling of indexes and constraints and types in tableInfo() (fixes BC break) - do not set nested transaction error if error is expected - explictly default to NULL when the column is NULLable and no default is set (related to Bug #8359) - added support for case insensitive matching via ILIKE in matchPattern() - added getAsKeyword() for generating "AS" keyword as required by the RDBMS - return an error if a named placeholder name is used twice inside a single statement - add support for multi column PRIMARY KEYs in createTable() - added lower() and upper() to the function module - moved escaping tests to datatype tests - added summary at the end of a test run for each RDBMS - explicitly pass the php_type var in all internal loadModule() calls - added support for 'primary' option in createTable() - expanded NULL tests - fixed handling return values when disable_query is set in _doQuery() and _execute() - check if safe_mode is enabled in fileExists() to determine what algo to use (Bug #8296) - added a test case for updating LOBs - added rtrim test case for LOBs - if result types are specified only rtrim() type 'text' fields (otherwise it will mean that for some drivers LOB's would get rtrimmed) - phpdoc fixes to the convertResult*() methods - added execParam() method and code tweaks for get*() methods in the Extended module - removed redundant assertions when testing if a given feature is unsupported - removed tests of portability off (users should just set the given portability setting they want to test in their test_setup.php) open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - expand charset support in schema management and result set handling (Request #4666) - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - expand length/scale support for numeric types (Request #7170) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), LENGTH() etc.) - add support for database/table/row LOCKs - add support for FOREIGN KEYs and CHECK (ENUM as possible mysql fallback) constraints - generate STATUS file from test suite results and allow users to submit test results - add a package2.xml and explore use of install groups (pear install MDB2#mysql) - add support for full text index creation and querying - add tests to check if the RDBMS specific handling with portability options disabled behaves as expected 2.2.0 2.2.0 stable stable 2006-07-23 BSD License - added MDB2_AUTOQUERY_SELECT (Request #7817) - added nested transaction support (inspired by ADODB's smart transactions) but expanded to optionally use SAVEPOINTs *EXPERIMENTAL* beginNestedTransaction(), completeNestedTransaction(), failNestedTransaction(), getNestedTransactionError() - inTransaction() will now return an integer with the nested transaction depth if a nested transaction has been started - added setTransactionIsolation() - added savepoint support to beginTransaction(), commit() and rollback() - added Native base class for consistency - added missing colnum parameter to queryOne() [used by getOne()] - added new tests for get*() Extended module methods - fixed missing db variable from getValidTypes() - added testing of a prepared statement with no parameters - added handling of empty result sets to result set verification in the test suite - oci8 and ibase (and possibly other rdbms) do not like freeing the statement before reading the result set (Bug #8068): * moved statement freeing after reading the result set in get*() Extended methods * bypass prepared statement API for queries without parameters in autoExecute() (this means you cannot use parameters with SELECT statements in autoExecute() on the above mentioned platforms) - use data type callback in getValidTypes() - fixed identifier quoting in buildManipSQL() for SELECT statements (thx Kailoran) - phpdoc and cosmetic fixes in limitQuery() - added matchPattern() and patternEscapeString(), escapePattern() *EXPERIMENTAL* - added ability to escape wildcard characters in escape() and quote() - added debug() call at the end of a query/prepare/execute calling (Request #7933) - added context array parameter to debug() and make use of it whereever sensible - added optional method name parameter to raiseError() and use whereever possible - added a new option "debug_expanded_output" which needs to be set to true to get additional context information and to get "post" callback calls - added testPortabilityOptions() - set length of 8 and fixed for user_password in the test suite - reworked tableInfo() to use a common implementation based on getTableFieldDefinition() when a table name is passed (Bug #8124) - disconnect after changing database/DSN (otherwise transactions may be left open) open todo items: - handle autoincrement fields in alterTable() - add length handling to LOB reverse engineering - expand charset support in schema management and result set handling (Request #4666) - add EXPLAIN abstraction - add cursor support along the lines of PDO (Request #3660 etc.) - expand length/scale support for numeric types (Request #7170) - add PDO based drivers, especially a driver to support SQLite 3 (Request #6907) - add support to export/import in CSV format - add more functions to the Function module (MD5(), IFNULL(), LENGTH() etc.) - add support to generating "AS" keyword if required - add support for database/table/row LOCKs - add ActiveRecord implementation (probably as a separate package) - add support for FOREIGN KEYs and CHECK (ENUM as possible mysql fallback) constraints - extended to support for case insensitive matching via ILIKE/collate in matchPattern() - generate STATUS file from test suite results and allow users to submit test results 2.1.0 2.1.0 stable stable 2006-06-15 BSD License - removed bogus code from execute() - new test case for floats/decimals and locale - reworked fix for float/decimal handling - expanded scientific notation handling - fixed several minor issues with the datatype tests - removed use of "*" in all places in the test suite that are followed by a fetch - tweaked handling of free() for prepared statements - return error if a prepared statement is attempted to be free'ed or executed again - added result_wrap_class param to limitQuery() - added parameter to not quote return value of getBeforeId() - added setCharset() - enable transactions by default - added decimal reverse engineering test - fixed parameter order in assertions in reverse engineering fields tests - generalized quoteIdentifier() with a property - switched most array_key_exists() calls to !empty() to improve readability and performance - fixed a few edge cases and potential warnings - added ability to rewrite queries for query(), exec() and prepare() using a debug handler callback - added 'datatype_map' option (Request #7797) - added reverse parameter to getColumnNames() - added 'datatype_map_callback' option - added getValidTypes() method to handle additional types from the 'datatype_map' option - set last_query in _execute() to prepared statement (Bug #7856) - adding random function emulation to generate a float between 0 and 1 - explicitly fetch row id = 1 in LOB tests - cosmetic fix to prepare() (Bug #7883) - bumped PHP dependency to 4.3.2 because of LOB stream support open todo items: - handle autoincrement fields in alterTable() - add support for ADODB style "smart transactions": http://phplens.com/lens/adodb/docs-adodb.htm#ex11 - add length handling to LOB reverse engineering 2.0.3 2.0.3 stable stable 2006-05-22 BSD License - fix float/decimal handling (Request #3021) - default to decimal for double (instead of float) - serialize arrays when no type is explicitly given - phpdoc fixes - expanded FLOAT test - added support for identifier quoting (Request #7671) - added test class for the Extended module - added support for DELETE in auto*() methods (Request #5345) - added "emulate_prepared" option to force prepared statement emulation open todo items: - handle autoincrement fields in alterTable() - add support for ADODB style "smart transactions": http://phplens.com/lens/adodb/docs-adodb.htm#ex11 - add length handling to LOB reverse engineering 2.0.2 2.0.2 stable stable 2006-05-14 BSD License - phpdoc and folding mark fixes (thx Laurent and Stoyan) - added getTriggerDefinition() in the Reverse module - added listTableViews() in the Manager module - explicitly set is_manip parameter to false for transaction debug calls - various minor tweaks to error messages, phpdoc and adding stub methods to the common driver - added row seeking support to fetchOne() - added ability to hint that a constraint is a primary key in dropConstraint() - since we no longer complain about primary keys not being called primary we can remove the necessary hacks in the test suite - improved LOB tests in the test suite - do not require Manager module for core API tests - added support for fixed and variable types for 'text' in declarations, as well as in reverse engineering (Request #1523) - updated MAINTAINERS to current status - readded STATUS document, still needs to be filled with content - tweaked handling of error objects inside raiseError() - made _doQuery() return a reference - added userinfo's to all raiseError calls that previously had none - added bindValueArray() - use bindValueArray() instead of bindParamArray() in all internal calls - removed the skeleton drivers, since its too much work to maintain them and there are plenty of sample drivers to look at - fixed example due to API change in MDB2_Schema (Bug #7575) - added 'prepared_statements' supported meta data setting - do not retrieve lob in the stream constructor and streams_eof() - strip of file:// prefix in writeLOBToFile() - typo fix ressource/resource in LOB array - removed lob property from the LOB streams wrapper (references are always shaky business in PHP so lets use them as little as possible) - fixed _destroyLOB() API to match other private LOB methods - fixed phpdoc comments of all private LOB methods - typo fix in autoincrement test - reworked index/constraint creation to not affected unnecessary reverse tests Note: Due to changes in generation of declaration statements you may need to update your test databases (for example by rerunning the MDB2_Schema installation using the CVS version or a release greater than 0.5.0) open todo items: - handle autoincrement fields in alterTable() - add support for ADODB style "smart transactions": http://phplens.com/lens/adodb/docs-adodb.htm#ex11 2.0.1 2.0.1 stable stable 2006-04-16 BSD License - added new comprehensive tests for the Reverse module - fixed testcases to pass for mysql[i] (needs pk's to be called "primary") .. sqlite is probably severely broken for these tests - added ability to specify port number when using unix sockets in MDB2::parseDSN() (bug #5982) - added test for multi_query option - typo fix in get constraint test - use ugly fopen() hack in fileExists() http://marc.theaimsgroup.com/?l=pear-dev&m=114148949106207&w=2 - allow "." and "$" in sequence name (bug #7081) - aligned _modifyQuery() signature and phpdoc - added inTransaction() to determine if a transaction is currently open - added support for tabe options in createTable() (bug ##7079) - make it possible to overwrite the error code-message map - added sample sqlite in memory dsn to php5 example - added 'result_introspection' supported metadata support - added bindValue() method - use MDB2_PREPARE_MANIP where we previously were using false - fixed default values for date and timestamp - if MDB2_PORTABILITY_EMPTY_TO_NULL is set change '' to ' ' in _getDeclaration() - refactored class loading into MDB2::loadClass() - properly quote CURRENT_* for temporal types (bug #6416) - added connected_server_info to cache server info in getServerInfo() - reset all connection related properties in disconnect() - separated result_buffering and prefetching by adding the new result_prefetching option - set error code in all raiseError() calls - added support for length in reverse engineering of integer fields - improve test suite documentation open todo items: - handle autoincremement fields in alterTable() 2.0.0 2.0.0 stable stable 2006-02-09 BSD License - handle null as resource when disable_query option is enabled in result object 2.0.0RC5 2.0.0RC5 beta beta 2006-02-05 BSD License - expanded testing of prepared queries (out of order binding, escape characters inside the string, lobs without named parameters that match the field name) - removed ugly hack for quote parameter in quote() since it was insufficient (escaping also needs to be prevented) - added support for out of order parameter binding in prepared queries - expanded testing of prepared queries (out of order binding, escape characters inside the string, lobs without named parameters that match the field name) - reset row_limit and row_offset after calling prepare() just like we do for query() and exec() - cosmetic fix (removed "row_" prefix from "row_limit" and "row_offset") - now using INT/TINYINT/SMALLINT by default instead of CHAR(1) for the boolean datatype (BC BREAK!) - added MDB2_datatype_testcase to test suite - support an arbitrary number of arguments in concat() - add property phpdoc comments to LOB.php 2.0.0RC4 2.0.0RC4 beta beta 2006-01-13 BSD License - explicitly pass if the module is phptype specific in all loadModule calls (bug #6226) - some cleanups in loadModule() - eliminate possible warnings in execute() and _assignBindColumns() - do not silence includes when debug option is enabled - fixed serious bug in autoincrement test - added dbsyntax to getDSN() string output (feature request #6463) - fixed signature of executeStoredProc() - nextResult() returns false if there are no more result sets to read - renamed _isIndexName() to _fixIndexName() - _fixIndexName() now just attempts to remove possible formatting - renamed _isSequenceName() to _fixSequenceName() - _fixSequenceName() now just attempts to remove possible formatting, and only returns a boolean if no formatting was applied when the new "check" parameter is set to true 2.0.0RC3 2.0.0RC3 beta beta 2005-12-30 BSD License - added error handling when result set introspection is not supported in _wrapResult() - fixed example - removed peardb wrapper (its broken, unmaintained and probably unused) - added new example using php5 only features - MDB2_OK is now a boolean true, instead of integer 1 - types can now always be keyed by name or by order - renamed setResultTypes() in the datatype module to checkResultTypes() and modified the signature accordingly - removed no longer used MDB2::isManip() 2.0.0RC2 2.0.0RC2 beta beta 2005-12-28 BSD License - fixed testNow() to use the datatype abstraction - fixed testGetTableFieldDefinition() to use a blob column instead of a clob since we default to blob when we cannot differntiate between blob and clob - split up alterTable() testing into two separate test methods - removed errorNative() was never implemented, use errorInfo() instead - fixed major bug in _fixResultArrayValues() that would lead to performance or incorrect application of portability features - phpdoc fixes in LOB.php, Iterator.php, Date.php and Extended.php - removed not required prev() and hasPrev() from the SeekableIterator implementation 2.0.0RC1 2.0.0RC1 beta beta 2005-12-21 BSD License - added numerous testcases in the manager and reverse module - unified case fixing in the list*() methods - case-insensitive comparison in _isSequenceName() - added getConnection() to fetch a native connection resource - split index and constraint handling - quote identifiers where possible inside the manager methods depending on the new 'quote_identifier' option (defaults to off) - refactored get*Declaration() methods to use getTypeDeclaration() - setting in_transaction to false on disconnect - store if type has changed in compareDefinition() - added new Function modules to handle difference in SQL functions - added verious new test cases - refactored test cases to use an MDB2_testcase base class - allow empty field parameter in get*ID() methods (bug #5791) - tweaked error messages for file loads - split off manipulation queries into exec() method from the query() method *BC BREAK* - only if result_types is set to false in prepare() method the query will be handled as a DML statement *BC BREAK* - use a proper default value if a field is set to not null in _getDeclaration*() (bug #5930) - added getServerVersion() - renamed defaultOutput() to getDefaultOutput() *BC BREAK* - use tableInfo() to automatically determine the result types if type is set to true - reworked file loading to work around issues in safe_mode with MDB2::fileExists() (bug #6226) - no need to return by reference in getConnection() (it even seems to work for mysqli that has objects and not resource connections) - added "idxname_format" as option similar to "seqname_format" - fixed bug in API calls inside autoExecute() (bug #6286) 2.0.0beta6 2.0.0beta6 beta beta 2005-10-16 BSD License Warning: this release features numerous BC breaks! There have been considerable improvements to the datatype, manager and reverse modules. Furthermore preliminary support for auto increment and primary keys has been added. Please note that making a field auto increment implies a single column primary key on this field. - increased php dependency to 4.3.0 due to the usage of the streams API since beta5 - moved logic from MDB2::connect() to MDB2::factory(), the only difference is that MDB2::connect will immediatly try to connect to the database - MDB2::singleton now uses MDB2::factory() - added support for auto increment and primary key in schema. (mysql[i]) - alterTable now needs the full definition to work (use getTableFieldDefinition from Reverse module if you do not have a definition at hand) this eliminates the need of the declaration part in the alterTable array. - nicer test chooser. Added some js magic to [un]select all the tests in a group - fixed typo in _getTextDeclaration() - fix PHP4.4 breakage - ensure that types and result_types property in the statement class is an array (bug #4695) - added support for fetchmode in the iterator class and for any other result wrapper class (bug #4685) - moved getInsertID() into core as lastInsertID() - moved getBeforeID() and getAfterID() from core into the extended module - added base class for all modules (which provides getDBInstance()) - added free() method to remove an instance from the global instance array - removed schema manager related error codes from MDB2::errorMessage() - dont set the include path in test suite (people can do that in test_setup.php) - added missing default numRows() method - added hack into stream_eof() to handle the BC break in 5.0.x - removed uncessary duplicate quoting in quote() in the peardb wrapper (bug #5195) - warning fix in BC hack of connect() in the peardb wrapper - tweaked error message in setResultTypes() - removed PDO compatibility code in bindParam and bindCol, now using 0-index numeric keys again - expect keys in type arrays the same way as they are passed for the values in execute() and bindParamArray() - add s pattern modifier to preg_replace() call for parameter searches in prepare() (bug #5362) - moved all private fetch mode fix methods into _fixResultArrayValues() for performance reasons - added new portability fetch mode MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES (to remove database/table qualifiers from assoc indexes) - renamed MDB2_PORTABILITY_LOWERCASE to MDB2_PORTABILITY_FIX_CASE and use 'field_case' option to determine if to upper- or lowercase (CASE_LOWER/CASE_UPPER) - ensure that fetchAll always returns an array() even if the result set was empty - use array_key_exists() instead of isset() where possible - changed structure of field add/remove/change in alterTable() to match MDB2_Schema - added default values for supported property - reworked supports() to return the given value and also return errors for non existant support feature - reworked subSelect() to use the 'emulated' supports() return value - removed implementation of createIndex() (now every driver needs to implement it themselves) - sync fileExists with the LiveUser one, explode instead of split and is_readable instead of file_exists. - tweaked compare method family to better deal with optional properties open todo items: - add test cases for the various module methods - add getServerVersion() 2.0.0beta5 2.0.0beta5 beta beta 2005-06-08 BSD License Warning: this release features numerous BC breaks to make the MDB2 API be as similar as possible as the ext/pdo API! The next release is likely to also break BC for the same reason. Check php.net/pdo for information on the pdo API. - refactored LOB support (BC breaks) - moved all drivers into separate packages MDB2_Driver_* (BC break) - bindParam() and bindColumn() are now 1-indexed (BC break) - removed special handling for day light saving time (bug #4341) (BC break) - ensure SQL injection protection in all _quote() methods (was missing in some decimal, float, time, date and timestamp implementations) - renamed getRowCount() to rowCount() for PDO compliance (BC break) (doesnt take into account the offset anymore) - added new quote() parameter to remove quotes (ugly hack will get cleaned up) - renamed execute() to _execute() since common provides some common functionality via execute() - fixed some issues regarding limit/offset in prepared statements - fixed bug in _assignBindColumns() when using associative fetches - support numeric and string keys in types array for prepared queries - call trigger error if __call() is unable to find a method in any of the modules - work around php5 bugs in the test suite 2.0.0beta4 2.0.0beta4 beta beta 2005-04-29 BSD License Warning: this release features numerous BC breaks to make the MDB2 API be as similar as possible as the ext/pdo API! The next release is likely to also break BC for the same reason. Check php.net/pdo for information on the pdo API. - fixed bugs in MDB2_Extended::buildManipSQL() introduced in latest tweaks (bug #3725) - mysqli has connection objects instead of resources - fix mssql tableInfo() so flags are returned (bug #3691) - fixed bug in handling of force_array when 2 or less columns are fetched in fetchAll() - added map error message for sqlite multi-column unique constraints. - added listUsers(), listViews(), listFunctions() to oracle manager - added listFunctions() to pgsql manager - updated listViews() in pgsql manager - added __call() support for module handling - mysql driver now uses mysqli implementations where feasible - ensure that internal calls to query dont wrap the result - for some reason mysqli didnt like SELECT LAST_INSERT_ID() - fixed bug in table alteration when only an index was added - updated pgsql API calls to 4.2.0 recommended names (bug #3904) - moved logic to compareDefinitions from the Manager into the Datatype module to increase flexibility - extended MDB2::isError() to be able to handle an array or codes - added error handling into autoPrepare() and autoExecute() - migrade all MDB2::isError calls that dont check for specific errors codes to PEAR::isError - don't pass new_link to mysql_pconnect() (bug #3993) - use MDB2::raiseError() instead of MDB2_Driver_Common::raiseError() - do not disable result wrapping when doing internal calls to query() (bug #3997) - _wrapResult() now ensures that the result class is an instance of MDB2_Result_Common - unbundled the MDB2_Tools_Manager into a separate package PEAR::MDB2_Schema - improved getTableFieldDefinition() and moved native type mapping to the datatype module mapNativeDatatype() method (mysql, sqlite, pgsql and ibase drivers) - fixes for listTables() in sqlite and pgsql driver - ensure that mysql drivers use the dummy_primary_key property - severely reworked how data is fetched and buffered and freed in the iterator - added mapNativeDatatype() to ibase driver - getTypeDeclaration() => _getTypeDeclaration() in ibase driver - cosmetic fixes and tweaks (replace(). fetchOne() ..) - renamed 'seqname_col_name' option to 'seqcol_name' - moved schema documentation, xml_reverse_engineering.php, MDB.dtd and MDB.xls to MDB_Schema package - Mysqli: implicit sequence is named as table by default - Mysqli: text types now map to clob first - ensure that types are numerically keyed in setResultTypes() - added caching to getColumnNames() - added bindColumn() support - use MDB2_Schema::factory() - phpdoc fixes in regards to flipped fetchmode - remove renegate mysql code in sqlite driver 2.0.0beta3 2.0.0beta3 beta beta 2005-03-06 BSD License Warning: this release features numerous BC breaks to make the MDB2 API be as similar as possible as the ext/pdo API! The next release is likely to also break BC for the same reason. Check php.net/pdo for information on the pdo API. Oracle NULL in LOB fields is broken. The fbsql and mssql drivers are likely to be broken as they are largely untested. MDB2 static class: - "xxx" out password on connect error in MDB2::connect() - MDB2::isError now also optionally accepts and error code to check for - added LOAD DATA (port from DB) and SET to MDB2::isManip() All drivers: - use __construct() (PHP4 BC hacks are provided) - allow null values to be set for options - ensure we are returning a reference in all relevant places - allow errorInfo() to be called when no connection has been established yet - use MDB2_ERROR_UNSUPPORTED instead of MDB2_ERROR_NOT_CAPABLE in common implementations - readded MDB2_Error as the baseclass for all MDB2 error objects - updated error mappings from DB - added MDB2_Driver_Common::getDatabase(); - reworked dsn default handling - added ability to "xxx" out password in getDSN() - use _close() method in several places where they previously were not used - removed redundant code in _close() that dealt with transaction closing already done in disconnect() - if the dbsyntax is set in the dsn it will be set in the dbsyntax property - only disconnect persistant connections if disconnect() has been explicitly called by the user - instead of having a generic implemention of disconnect() we will rename _close() to disconnect() to overwrite the generic implementation - added support for 'new_link' dsn option for all supported drivers (mysql, oci8, pgsql) - transaction API moved over to PDO: removed autoCommit(), added beginTransaction() and refactored commit() (it doesn't start a new transaction automatically anymore) - reworked handling of uncommited transaction for persistant connections when a given connection is no longer in use - added 'disable_query' option to be able to disable the execution of all queries (this might be useful in conjuntion with a custom debug handler to be able to dump all queries into a file instead of executing them) - removed affectedRows() method in favor of returning affectedRows() on query if relevant - added generic implementation of query() and moved driver specific code into _doQuery() - added _modifyQuery() to any driver that did not yet have it yet - standaloneQuery() now also supports SELECT querys - remove redundant call to commit() since setting autoCommit() already commits in MDB2::replace() - refactored standaloneQuery(), query(), _doQuery(), _wrapResult(); the most important change are: result are only wrapped if it is explicitly requested standaloneQuery() now works just as query() does but with its own connection - allowing limits of 0 in setLimit() - explicitly specify colum name in sequence emulation queries - added getBeforeId() and getAfterId() - added new supported feature 'auto_increment' - added default implementation for quoteCLOB() and quoteBLOB() - reworked quote handling: moved all implementation details into the extension, made all quote methods private except for quote() itself, honor portability MDB2_PORTABILITY_EMPTY_TO_NULL in quote(), removed MDB2_TYPE_* constants - reworked get*Declaration handling: moved all implementation details into the extension, made all quote methods private except for quote() itself - placed convert methods after the portability conversions to ensure that the proper type is maintained after the conversion methods - dont convert fetched null values in the Datatype module - removed executeParams() and moved executeMultiple() from extended module - updated tableInfo() code from DB - made LIMIT handling more robust by taking some code from DB All drivers result: - performance tweak in fetchCol() - added MDB2_FETCHMODE_OBJECT - added MDB2_Driver_Result_Common::getRowCounter() - added rownum handling to fetchRow() - removed fetch() and resultIsNull() All drivers prepared statements - moved prepare/execute API towards PDO - setParamsArray() can now handle non ordered arrays - removed requirement for LOB inserts to pass the parameters as an array - placeholders are now numbered starting from 0 (BC break in setParam() !) - queries inside the prepared_queries property now start counting at 1 (performance tweak) - refactored handling of filename LOB values (prefix with 'file://') - removed _executePrepared(), drivers need to overwrite execute() for now on - add support for oracle style named parameters and modified test suite accordingly MySQL driver: - improved handling of MDB2_PORTABILITY_LOWERCASE in all the reverse methods inside the mysql driver to work coherently - fixed several issues in the listTablefields() method of manager drivers MSSQL driver: - added code in MDB2_Driver_mssql::connect() to better handle date values independant of ini and locale settings inside the server - use comma, rather than colon, to delimit port in MDB2_driver_mssql::connect(). Bug 2140. (danielc) - unified mssql standalone query with sqlite, mysql and others (not tested on mssql yet, but since mssql automatically reuses connections per dsn the old way could gurantee anything different from happening) PgSQL driver: - use track_errors to capture error messages in MDB2_driver_pgsql::connect(). Bug 2011. (danielc) - add port to connect string when protocol is unix in MDB2_driver_pgsql::connect(). Bug 1919. (danielc) - accommodate changes made to PostgreSQL so "no such field" errors get properly indicated rather than being mislabeled as "no such table." (danielc) - added "permission denied" to error regex in pgsql driver. Bug 2417. (stewart_linux-org-au) OCI8 driver: - fixed typo in MDB2_Driver_Manager_oci8::listTables() (fix for bug #2434) - added emulate_database option (default true) to the Oracle driver that handles if the database_name should be used for connections of the username - oci8 driver now uses native bind support for all types in prepare()/execute() Interbase driver: - completely revised ibase driver, now passing all tests under php5 Frontbase driver: - fbsql: use correct error codes. Was using MySQL's codes by mistake. MySQLi driver: - added mysqli driver (passes all tests, but doesnt use native prepare yet) DB wrapper - fixed a large number of compatibility issues in the PEAR::DB wrapper Iterator - fixed several bugs and updated the interface to match the final php5 iterator API - buffered result sets now implements seekable - removed unnecessary returns - throw pear error on rewind in unbuffered result set - renamed size() to count() to match the upcoming Countable interface Extended module: - modified the signature of the auto*() methods to be compatible with DB (bug #3720) - tweaked buildManipSQL() to not use loops (bug #3721) MDB_Tools_Manager - updated raiseError method in the Manager to be compatible with XML_Parser 1.1.x and return useful error message (fix bug #2055) - major refactoring of MDB2_Manager resulting in several new methods being available - fixed error in MDB2_Manager::_escapeSpecialCharacter() that would lead to incorrect handling of integer values (this needs to be explored in more detail) - several typo fixes and minor logic errors (among others a fix for bug #2057) - moved xml dumping in MDB2_Tools_Manager into separate Writer class - fixed bugs in start value handling in create sequence (bug #3077) 2.0.0beta2 2.0.0beta2 beta beta 2004-04-25 BSD License The core of MDB2 is now fairly stable API-wise. The modules, especially the manager and reverse module, might see some API refinement before the first stable release. - added listTables() and listTableFields() methods to MDB2_Driver_Manager_mssql and MDB2_Driver_Manager_oci8 - reversed parameter order of getValue(), type parameter is now optional and will then be autodetected (BC break!) - renamed get*Value() to quote*() (BC break!) - fixed LOB management in MDB2_Driver_ibase - moved getOne, getRow, getCol, getAll back into the exteneded module (most users should be able to move to the queryOne, queryRow, queryCol and queryAll equivalent) (BC break!) - added getAssoc to the extended module - fixed bug in MDB2_Driver_Datatype_Common::implodeArray() - added sequence_col_name option to make the column name inside sequence emulation tables configurable - fixed a bug in the MDB2_Driver_oci8 and MDB2_Driver_ibase buffering emulation when using limit queries - removed MDB2_PORTABILITY_NULL_TO_EMPTY in favor of MDB2_PORTABILITY_EMPTY_TO_NULL this means that DB and MDB2 work exactly the opposite now, but it seems more efficient to do things the way Oracle does since this is the RDBMS which creates the original issue to begin with (BC break!) - fixed a typos in getAll, getAssoc and getCol - test suite: moved set_time_limit() call to the setup script to be easier to customize - renamed hasMore() to valid() due to changes in the PHP5 iterator API (BC break!) - renamed toString() to __toString() in order to take advantage of new PHP5 goodness and made it public - MDB2_Driver_Datatype_Common::setResultTypes() can now handle missing elements inside type arrays: array(2 => 'boolean', 4 => 'timestamp') - fixed potential warning due to manipulation query detection in the query*() and the get*() query+fetch methods - added tests for fetchAll() and fetchCol() - performance tweaks for fetchAll() and fetchCol() - fixed MDB2_Driver_Manager_mysql::listTableIndexes() - fixed MDB2_Driver_Common::debug() - renamed MDB2::isResult() to MDB2::isResultCommon() - added base result class MDB2_Result from which all result sets should be inherited and added MDB2::isResult() which checks if a given object extends from it - added 'result_wrap_class' option and optional parameter to query() to enable wrapping of result classes into an arbitrary class - added $result_class param to all drivers where it was missing from the query() and _executePrepared() methods - applied several fixes to the PEAR::DB wrapper - fixed a typo in MDB2_Driver_Reverse_pgsql::tableInfo() 2.0.0beta1 2.0.0beta1 alpha alpha 2004-03-12 BSD License - fixed bug in MDB2::singleton - fixed minor bugs in prepare/execute - added PEAR::DB wrapper (not working yet) - fixed several bugs in the ibase driver - fixed several PHP5 related issues - fixed bug in sequence creation on MySQL - fixed issues with nextid() ondemand handling in conjunction with currId() - added native currId() implementation for the Oracle driver - fixed sqlite driver (passes all but the REPLACE test due to a conformance issue in sqlite itself) - removed decimal_factor property to allow changing of decimal_places option - using native escape string methods in sqlite and mysql driver - fixed minor conformance issues in tableInfo() in the oci8 and mysql driver - removed optimize option and added portability option instead (ported from DB) - added quoteIdentifier() method (ported from DB) - added STATUS document to make the status of the drivers more transparent - fixed a few bugs in querysim driver - fixed issue in mysql reverse engineering: ensuring the correct case is used when doing assoc fetches based on portability flag setting - updated reverse engineering script to the new MDB2 API - removed broken implementations of currId() in the mssql and fbsql driver - fixed a few instances of MDB_Common to the new class name of MDB_Driver_Common 2.0.0alpha1 2.0.0alpha1 alpha alpha 2004-01-05 BSD License This is the first alpha release of MDB2 2.0. MDB2 2.x breaks backwards compatibility in many ways in order to simplify the API for both users and drivers developers. Please note that currently only the MySQL, the PostGreSQL and the Oracle driver have been tested to pass the test suite. Here follows a short list of the most important changes: - all code that is not necessary for basic operation is now separateed into separate modules which can be loaded with the loadModule() method - all datatype related methods have been moved to a dataype module with the notable exception of getValue() and the newly introduced getDeclaration() - added extended module for highlevel methods - all manager method are no longer available in the core class and or now only available in the manager module - all reverse engineering methods have been taken from the manager class and are now available through the reverse module - a new module has been added to allow the addition of methods with RDBMS specific functionality (like getting the last autoincrement ID) - LOB handling has been greatly simplified - several methods names have been shortend - the fetch.+() methods do not free the result set anymore - the Manager and the reverse_engineer_xml_schema have been moved into a Tools directory - all parameters are now lowercased with underscores as separators - all drivers now support all of the dsn options that PEAR DB supports - several methods have been removed because they offered redundant functionality - changed prepare API type is now passed to prepare and not to setParam*() - results are now wrapped inside objects and all methods which operate on resultsets have been moved into respecitive classes - there are two types of result object: buffered (default) and unbuffered - totally rewrote buffering and limit emulation