package.xml0000664000175000017500000007703112205351473011311 0ustar janjan Horde_Db pear.horde.org Db Horde Database Libraries Horde database/SQL abstraction layer Mike Naberezny mnaberez mike@naberezny.com yes Chuck Hagenbuch chuck chuck@horde.org yes Jan Schneider jan jan@horde.org yes 2013-08-22 2.0.4 1.2.0 stable stable BSD-2-Clause * [jan] Fix schema version table names when using horde-db-migrate-component. 5.3.0 1.7.0 Horde_Date pear.horde.org 2.0.0 3.0.0alpha1 3.0.0alpha1 Horde_Exception pear.horde.org 2.0.0 3.0.0alpha1 3.0.0alpha1 Horde_Support pear.horde.org 2.0.0 3.0.0alpha1 3.0.0alpha1 Horde_Autoloader pear.horde.org 2.0.0 3.0.0alpha1 3.0.0alpha1 Horde_Cache pear.horde.org 2.0.0 3.0.0alpha1 3.0.0alpha1 Horde_Log pear.horde.org 2.0.0 3.0.0alpha1 3.0.0alpha1 Horde_Test pear.horde.org 2.1.0 3.0.0alpha1 3.0.0alpha1 mysql mysqli PDO 0.1.0 0.1.0 beta beta 2011-02-01 BSD-2-Clause * Initial release 1.0.0alpha1 1.0.0 alpha alpha 2011-03-08 BSD-2-Clause * First alpha release for Horde 4. * Add support for adding autoincrement to a column. 1.0.0beta1 1.0.0 beta beta 2011-03-16 BSD-2-Clause * Drop schema table when migrating down to schema version 0. * Don't try to create schema tables if they exist, to avoid irritating log entries. 1.0.0RC1 1.0.0 beta beta 2011-03-22 BSD-2-Clause * First release candidate for Horde 4. 1.0.0RC2 1.0.0 beta beta 2011-03-29 BSD-2-Clause * Second release candidate for Horde 4. * [jan] Don't throw exception when setting port number while using localhost in PDO MySQL driver (Bug #9738). * [jan] Rename pseudo-type primaryKey to autoincrementKey. 1.0.0 1.0.0 stable stable 2011-04-06 BSD-2-Clause * First stable release for Horde 4. * [jan] Add buildClause() from obsolete Horde_Sql. 1.0.1 1.0.0 stable stable 2011-04-20 BSD-2-Clause * [jan] Fix migration detection on Windows. * [jan] Fix setting incorrect charset in PDO drivers. 1.0.2 1.0.0 stable stable 2011-07-05 BSD-2-Clause * [jan] Convert host parameter from "localhost" to "127.0.0.1" in MySQL drivers (Request #9738). * [gwr] Renamed migration helper to avoid namespace clash with a similar tool from the horde base package. * [jan] Keep using the write backend in the same request after writing once with the split read/write driver. 1.0.3 1.0.0 stable stable 2011-07-27 BSD-2-Clause * [jan] Correctly format float values independently from the current locale (Bug #10371). 1.0.4 1.0.0 stable stable 2011-08-30 BSD-2-Clause * [jan] Fix migrations from primary keys that have not been created with Horde_Db in PostgreSQL. * [jan] Fix autoincrementKey sequences not incrementing after manually inserting key values in PostgreSQL. * [jan] Fix changeColumn() migration to autoincrementKey if no primary key exists yet. 1.0.5 1.0.0 stable stable 2011-08-31 BSD-2-Clause * [jan] Fix determination of last insert id on PostgreSQL up to 8.1. 1.1.0 1.1.0 stable stable 2011-09-20 BSD-2-Clause * [jan] Add methods to add and drop primary keys (Request #10469). * [gwr] Fix usage of Horde_Exception_Wrapped. * [jan] Fix setting unix socket in PostgreSQL DSN (bug-reports@flipjam.co.uk, Bug #10466). 1.1.1 1.1.0 stable stable 2011-10-11 BSD-2-Clause * [jan] More flexible conversion of charset names to native MySQL names. * [jan] Correctly escape binding characters in buildClause() (Bug #10610). * [jan] Fix transactions with MySQLi driver (Bug #10578). * [jan] Fix DISTINCT clause generation on PostgreSQL (Bug #10543). * [mms] Fix escaping binary data on Postgresql 9.1+ servers (Bug #10602). 1.1.2 1.1.0 stable stable 2011-11-22 BSD-2-Clause * [mms] Greatly reduce memory usage when working with binary data in PostgreSQL 9.0+ (Bug #10774). * [mms] Cache postgresql version. * [jan] Improve API documentation. 1.2.0 1.2.0 stable stable 2011-12-13 BSD-2-Clause * [gwr] Alternative API to support modified dates with SQLite as well. * [mjr] Add support for mediumtext and longtext field types. 1.2.1 1.2.0 stable stable 2012-03-20 BSD-2-Clause * [jan] Fix escaping of binary values in SQLite. 1.2.2 1.2.0 stable stable BSD-2-Clause * [mms] Fix resetting postgresql error reporting. * [mms] Fix Horde_Db_Adapter_Base_Table#offsetExists(). * [mms] Fix addLock() command. 2.0.0alpha1 1.2.0 alpha stable 2012-07-05 BSD-2-Clause * First alpha release for Horde 5. 2.0.0beta1 1.2.0 beta stable 2012-07-19 BSD-2-Clause * First beta release for Horde 5. * [jan] Allow for nested transactions. 2.0.0RC1 1.2.0 beta stable 2012-10-26 BSD-2-Clause * [mms] Fix resetting postgresql error reporting. * [mms] Fix Horde_Db_Adapter_Base_Table#offsetExists(). * [mms] Fix addLock() command. 2.0.0 1.2.0 stable stable 2012-10-30 BSD-2-Clause * First stable release for Horde 5. 2.0.1 1.2.0 stable stable 2012-11-19 BSD-2-Clause * [mms] Use new Horde_Test layout. 2.0.2 1.2.0 stable stable 2013-03-05 BSD-2-Clause * [jan] Improve unit tests. 2.0.3 1.2.0 stable stable 2013-05-03 BSD-2-Clause * [mms] Fix storing schema information in Horde_Cache. * [jan] Use LONGBLOB columns in MySQL when creating binary fields (l.kiraly@madalbal.hu, Bug #12195). * [jan] Fix updating existing rows with default values when adding columns in PostgreSQL (Bug #12101). 2.0.4 1.2.0 stable stable 2013-08-22 BSD-2-Clause * [jan] Fix schema version table names when using horde-db-migrate-component. Horde_Db-2.0.4/bin/horde-db-migrate-component0000775000175000017500000000602212205351473017101 0ustar janjan#!/usr/bin/env php * @author Jan Schneider */ require_once 'Horde/Autoloader/Default.php'; // Parse command line arguments array_shift($_SERVER['argv']); $args = $params = array(); foreach ($_SERVER['argv'] as $arg) { if (substr($arg, 0, 2) == '--') { $param = substr($arg, 2); $param = explode('=', $param, 2); if (count($param) != 2) { die("The --${param[0]} parameter must have a value.\n"); } $params[$param[0]] = $param[1]; } else { $args[] = $arg; } } if (empty($args[1])) { die("horde-db-migrate-component [--parameter=value ...] directory module [(up|down|version) [debug]]\n"); } if (!isset($params['adapter'])) { die("The --adapter parameter is required. Other parameters may be required depending on the adapter.\n"); } $dir = $args[0]; $module = strtolower($args[1]); if (!is_dir($dir)) { die("$dir is not a migration directory"); } $action = 'up'; if (!empty($args[2])) { switch ($args[2]) { case 'up': case 'down': $action = $args[2]; break; default: $action = 'migrate'; $targetVersion = $args[2]; break; } } // Build Horde_Db adapter. $class = 'Horde_Db_Adapter_' . str_replace(' ', '_', ucwords(str_replace('_', ' ', basename($params['adapter'])))); if (!class_exists($class)) { die($params['adapter'] . " is not a valid adapter name.\n"); } unset($params['adapter']); try { $db = new $class($params); } catch (Exception $e) { die($e->getMessage() . "\n"); } if (!empty($args[3]) && strpos($args[3], 'debug') !== false) { $logger = new Horde_Log_Logger(new Horde_Log_Handler_Stream(STDOUT)); $db->setLogger($logger); } $logger = new Horde_Log_Logger( new Horde_Log_Handler_Stream( STDOUT, null, new Horde_Log_Formatter_Simple('%message%' . PHP_EOL))); $migrator = new Horde_Db_Migration_Migrator( $db, $logger, array('migrationsPath' => $dir, 'schemaTableName' => $module . '_schema_info')); echo 'Current schema version: ' . $migrator->getCurrentVersion() . "\n"; try { switch ($action) { case 'up': echo "Migrating DB up.\n"; $migrator->up(); break; case 'down': echo "Migrating DB down.\n"; $migrator->down(); break; case 'migrate': echo 'Migrating DB to schema version ' . $targetVersion . ".\n"; $migrator->migrate($targetVersion); break; } } catch (Exception $e) { die($e->getMessage() . "\n"); } echo 'Ending schema version: ' . $migrator->getCurrentVersion() . "\n"; Horde_Db-2.0.4/doc/Horde/Db/COPYING0000664000175000017500000000243012205351473014456 0ustar janjan Copyright 1999-2013 Horde LLC. All rights reserved. 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. 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 HORDE PROJECT 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. Horde_Db-2.0.4/doc/Horde/Db/README_TESTING0000664000175000017500000000240612205351473015503 0ustar janjan===================== Horde/Db Test Suite ===================== :Last update: 2009-01-04 :Authors: Chuck Hagenbuch :Contact: dev@lists.horde.org .. contents:: Contents .. section-numbering:: Defining adapters ================= As long as PHP has the PDO SQLite driver (which is enabled by default), the SQLite tests will always be run. This is possible using the sqlite:memory database; no file access or permissions are required. For the other adapters, the Horde_Db test suite looks for environment variables named DB_ADAPTER_$driverName_TEST_CONFIG. For the MySQLi driver, that would be DB_ADAPTER_MYSQLI_TEST_CONFIG. For the PDO PostgreSQL driver, that would be DB_ADAPTER_PDO_PGSQL_TEST_CONFIG, and so on. The value of the environment variable is a JSON string with the configuration array for the adapter. Here is an example for setting up a test DSN for the MySQL test database on localhost, connecting as the user horde_db with no password: {"username":"horde_db","dbname":"test","host":"localhost"} When running the test suite, any adapter for which a DSN is not found, or for which connecting to the defined DSN fails, a single instance of Horde_Db_Adapter_MissingTest will be included in the test suite run, with details on why the adapter was skipped. Horde_Db-2.0.4/doc/Horde/Db/TODO0000664000175000017500000000367212205351473014124 0ustar janjanMigrations ---------- - timestamp-based migrations like rails 2.1: http://guides.rubyonrails.org/migrations.html - replace backend-specific structuredump with a PHP/JSON dump of schema and data, including a corresponding importer. A simple YAML/JSON structure that represents the PHP objects (TableDefinition, ColumnDefinition, IndexDefinition - or alternately writes them out programmatically like a migration script) would make it easiest to reconstruct identical tables between multiple machines and multiple database types, and a simple JSON encoding of data (a single header row, followed by one JSON array per database row per line) would take care of most special characters and serialization issues, and allow for fast line-by-line reading. - add convenience methods of calling $t->string('foo'), instead of $t->column('string', 'foo'), inside migration scripts. Will need to introspect the nativeDatabaseTypes() list from the connection for this. Dates ----- - Handle Horde_Date objects in both input/output from Horde_Db. - what to do about '0000-00-00' with PostgreSQL? - http://dev.mysql.com/doc/refman/5.1/en/time-zone-support.html - integer sizing for mysql like the postgres backend does it? - port updates in schema_definitions.rb and schema_statements.rb - allow adding multiple indexes (and columns) at once, in an aggregate operation. at the abstract level this will just execute the changes one at a time; for databases like mysql that can run multiple changes at once, it will, achieving much better efficiency. - rdo: add the ability to load a table once, then use it repeatedly in a relationship (or to have it be a static array), instead of joining. - composite primary key support: what does http://compositekeys.rubyforge.org/ have that we don't? - port disable_referential_integrity? - port query_cache.rb - Support mysqlnd statistics: http://developers.sun.com/databases/articles/mysql_php4.html#7 Horde_Db-2.0.4/lib/Horde/Db/Adapter/Base/Column.php0000664000175000017500000002247612205351473017640 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Base_Column { protected $_name; protected $_type; protected $_null; protected $_limit; protected $_precision; protected $_scale; protected $_unsigned; protected $_default; protected $_sqlType; protected $_isText; protected $_isNumber; /*########################################################################## # Construct/Destruct ##########################################################################*/ /** * Constructor. * * @param string $name The column's name, such as "supplier_id" in * "supplier_id int(11)". * @param string $default The type-casted default value, such as "new" in * "sales_stage varchar(20) default 'new'". * @param string $sqlType Used to extract the column's type, length and * signed status, if necessary. For example * "varchar" and "60" in "company_name varchar(60)" * or "unsigned => true" in "int(10) UNSIGNED". * @param boolean $null Whether this column allows NULL values. */ public function __construct($name, $default, $sqlType = null, $null = true) { $this->_name = $name; $this->_sqlType = $sqlType; $this->_null = $null; $this->_limit = $this->_extractLimit($sqlType); $this->_precision = $this->_extractPrecision($sqlType); $this->_scale = $this->_extractScale($sqlType); $this->_unsigned = $this->_extractUnsigned($sqlType); $this->_type = $this->_simplifiedType($sqlType); $this->_isText = $this->_type == 'text' || $this->_type == 'string'; $this->_isNumber = $this->_type == 'float' || $this->_type == 'integer' || $this->_type == 'decimal'; $this->_default = $this->extractDefault($default); } /** * @return boolean */ public function isText() { return $this->_isText; } /** * @return boolean */ public function isNumber() { return $this->_isNumber; } /** * Casts value (which is a String) to an appropriate instance. */ public function typeCast($value) { if ($value === null) return null; switch ($this->_type) { case 'string': case 'text': return $value; case 'integer': return strlen($value) ? (int)$value : null; case 'float': return strlen($value) ? (float)$value : null; case 'decimal': return $this->valueToDecimal($value); case 'datetime': case 'timestamp': return $this->stringToTime($value); case 'time': return $this->stringToDummyTime($value); case 'date': return $this->stringToDate($value); case 'binary': return $this->binaryToString($value); case 'boolean': return $this->valueToBoolean($value); default: return $value; } } public function extractDefault($default) { return $this->typeCast($default); } /*########################################################################## # Accessor ##########################################################################*/ /** * @return string */ public function getName() { return $this->_name; } /** * @return string */ public function getDefault() { return $this->_default; } /** * @return string */ public function getType() { return $this->_type; } /** * @return int */ public function getLimit() { return $this->_limit; } /** * @return int */ public function precision() { return $this->_precision; } /** * @return int */ public function scale() { return $this->_scale; } /** * @return boolean */ public function isUnsigned() { return $this->_unsigned; } /** * @return boolean */ public function isNull() { return $this->_null; } /** * @return string */ public function getSqlType() { return $this->_sqlType; } /*########################################################################## # Type Juggling ##########################################################################*/ /** * Used to convert from BLOBs to Strings * * @return string */ public function binaryToString($value) { return (string)$value; } /** * @param string $string * @return Horde_Date */ public function stringToDate($string) { if (empty($string) || // preserve '0000-00-00' (http://bugs.php.net/bug.php?id=45647) preg_replace('/[^\d]/', '', $string) == 0) { return null; } $d = new Horde_Date($string); $d->setDefaultFormat('Y-m-d'); return $d; } /** * @param string $string * @return Horde_Date */ public function stringToTime($string) { if (empty($string) || // preserve '0000-00-00 00:00:00' (http://bugs.php.net/bug.php?id=45647) preg_replace('/[^\d]/', '', $string) == 0) { return null; } return new Horde_Date($string); } /** * @TODO Return a Horde_Date object instead? * * @param string $string * @return Horde_Date */ public function stringToDummyTime($value) { if (empty($string)) { return null; } return $this->stringToTime('2000-01-01 ' . $string); } /** * @param mixed $value * @return boolean */ public function valueToBoolean($value) { if ($value === true || $value === false) { return $value; } $value = strtolower($value); return $value == 'true' || $value == 't' || $value == '1'; } /** * @param mixed $value * @return decimal */ public function valueToDecimal($value) { return (float)$value; } /*########################################################################## # Protected ##########################################################################*/ /** * @param string $sqlType * @return int */ protected function _extractLimit($sqlType) { if (preg_match("/\((.*)\)/", $sqlType, $matches)) { return (int)$matches[1]; } return null; } /** * @param string $sqlType * @return int */ protected function _extractPrecision($sqlType) { if (preg_match("/^(numeric|decimal|number)\((\d+)(,\d+)?\)/i", $sqlType, $matches)) { return (int)$matches[2]; } return null; } /** * @param string $sqlType * @return int */ protected function _extractScale($sqlType) { switch (true) { case preg_match("/^(numeric|decimal|number)\((\d+)\)/i", $sqlType): return 0; case preg_match("/^(numeric|decimal|number)\((\d+)(,(\d+))\)/i", $sqlType, $match): return (int)$match[4]; } } /** * @param string $sqlType * @return int */ protected function _extractUnsigned($sqlType) { return (boolean)preg_match('/^int.*unsigned/i', $sqlType); } /** * @param string $fieldType * @return string */ protected function _simplifiedType($fieldType) { switch (true) { case preg_match('/int/i', $fieldType): return 'integer'; case preg_match('/float|double/i', $fieldType): return 'float'; case preg_match('/decimal|numeric|number/i', $fieldType): return $this->_scale == 0 ? 'integer' : 'decimal'; case preg_match('/datetime/i', $fieldType): return 'datetime'; case preg_match('/timestamp/i', $fieldType): return 'timestamp'; case preg_match('/time/i', $fieldType): return 'time'; case preg_match('/date/i', $fieldType): return 'date'; case preg_match('/clob|text/i', $fieldType): return 'text'; case preg_match('/blob|binary/i', $fieldType): return 'binary'; case preg_match('/char|string/i', $fieldType): return 'string'; case preg_match('/boolean/i', $fieldType): return 'boolean'; } } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Base/ColumnDefinition.php0000664000175000017500000001251112205351473021636 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Base_ColumnDefinition { protected $_base; protected $_name; protected $_type; protected $_limit; protected $_precision; protected $_scale; protected $_unsigned; protected $_default; protected $_null; protected $_autoincrement; /** * Constructor. */ public function __construct($base, $name, $type, $limit = null, $precision = null, $scale = null, $unsigned = null, $default = null, $null = null, $autoincrement = null) { // Protected $this->_base = $base; // Public $this->_name = $name; $this->_type = $type; $this->_limit = $limit; $this->_precision = $precision; $this->_scale = $scale; $this->_unsigned = $unsigned; $this->_default = $default; $this->_null = $null; $this->_autoincrement = $autoincrement; } /*########################################################################## # Public ##########################################################################*/ /** * @return string */ public function toSql() { $sql = $this->_base->quoteColumnName($this->_name) . ' ' . $this->getSqlType(); return $this->_addColumnOptions($sql, array('null' => $this->_null, 'default' => $this->_default)); } /** * @return string */ public function __toString() { return $this->toSql(); } /*########################################################################## # Accessor ##########################################################################*/ /** * @return string */ public function getName() { return $this->_name; } /** * @return string */ public function getDefault() { return $this->_default; } /** * @return string */ public function getType() { return $this->_type; } /** * @return string */ public function getSqlType() { try { return $this->_base->typeToSql($this->_type, $this->_limit, $this->_precision, $this->_scale, $this->_unsigned); } catch (Exception $e) { return $this->_type; } } /** * @return int */ public function getLimit() { return $this->_limit; } /** * @return int */ public function precision() { return $this->_precision; } /** * @return int */ public function scale() { return $this->_scale; } /** * @return boolean */ public function isUnsigned() { return $this->_unsigned; } /** * @return boolean */ public function isNull() { return $this->_null; } /** * @return boolean */ public function isAutoIncrement() { return $this->_autoincrement; } /** * @param string */ public function setName($name) { $this->_name = $name; } /** * @param string */ public function setDefault($default) { $this->_default = $default; } /** * @param string */ public function setType($type) { $this->_type = $type; } /** * @param int */ public function setLimit($limit) { $this->_limit = $limit; } /** * @param int */ public function setPrecision($precision) { $this->_precision = $precision; } /** * @param int */ public function setScale($scale) { $this->_scale = $scale; } /** * @param boolean */ public function setUnsigned($unsigned) { $this->_unsigned = $unsigned; } /** * @param boolean */ public function setNull($null) { $this->_null = $null; } /** * @param boolean */ public function setAutoIncrement($autoincrement) { $this->_autoincrement = $autoincrement; } /*########################################################################## # Schema Statements ##########################################################################*/ /** * @param string $sql * @param array $options */ protected function _addColumnOptions($sql, $options) { return $this->_base->addColumnOptions($sql, array_merge($options, array('column' => $this)) ); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Base/Index.php0000664000175000017500000000500212205351473017434 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Base_Index { /** * The table the index is on. * * @var string */ public $table; /** * The index's name. * * @var string */ public $name; /** * */ public $unique; /** * Is this a primary key? * * @var boolean */ public $primary; /** * The columns this index covers. * * @var array */ public $columns; /*########################################################################## # Construct/Destruct ##########################################################################*/ /** * Constructor. * * @param string $table The table the index is on. * @param string $name The index's name. * @param boolean $primary Is this a primary key? * @param boolean $unique Is this a unique index? * @param array $columns The columns this index covers. */ public function __construct($table, $name, $primary, $unique, $columns) { $this->table = $table; $this->name = $name; $this->primary = $primary; $this->unique = $unique; $this->columns = $columns; } /*########################################################################## # Accessor ##########################################################################*/ /** * @return string */ public function getName() { return $this->name; } /*########################################################################## # Casting ##########################################################################*/ /** * Comma-separated list of the columns in the primary key * * @return string */ public function __toString() { return implode(',', $this->columns); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Base/Schema.php0000664000175000017500000011076612205351473017603 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ abstract class Horde_Db_Adapter_Base_Schema { /** * A Horde_Db_Adapter instance. * * @var Horde_Db_Adapter_Base */ protected $_adapter = null; /** * List of public methods supported by the attached adapter. * * Method names are in the keys. * * @var array */ protected $_adapterMethods = array(); /*########################################################################## # Construct/Destruct ##########################################################################*/ /** * Constructor. * * @param Horde_Db_Adapter_Base $adapter A Horde_Db_Adapter instance. */ public function __construct(Horde_Db_Adapter $adapter) { $this->setAdapter($adapter); } /** * Setter for a Horde_Db_Adapter instance. * * @param Horde_Db_Adapter $adapter A Horde_Db_Adapter instance. */ public function setAdapter(Horde_Db_Adapter $adapter) { $this->_adapter = $adapter; $this->_adapterMethods = array_flip(get_class_methods($adapter)); } /*########################################################################## # Object factories ##########################################################################*/ /** * Factory for Column objects. * * @param string $name The column's name, such as "supplier_id" in * "supplier_id int(11)". * @param string $default The type-casted default value, such as "new" in * "sales_stage varchar(20) default 'new'". * @param string $sqlType Used to extract the column's type, length and * signed status, if necessary. For example * "varchar" and "60" in "company_name varchar(60)" * or "unsigned => true" in "int(10) UNSIGNED". * @param boolean $null Whether this column allows NULL values. * * @return Horde_Db_Adapter_Base_Column A column object. */ public function makeColumn($name, $default, $sqlType = null, $null = true) { return new Horde_Db_Adapter_Base_Column($name, $default, $sqlType, $null); } /** * Factory for ColumnDefinition objects. * * @return Horde_Db_Adapter_Base_ColumnDefinition A column definition * object. */ public function makeColumnDefinition( $base, $name, $type, $limit = null, $precision = null, $scale = null, $unsigned = null, $default = null, $null = null, $autoincrement = null) { return new Horde_Db_Adapter_Base_ColumnDefinition( $base, $name, $type, $limit, $precision, $scale, $unsigned, $default, $null, $autoincrement); } /** * Factory for Index objects. * * @param string $table The table the index is on. * @param string $name The index's name. * @param boolean $primary Is this a primary key? * @param boolean $unique Is this a unique index? * @param array $columns The columns this index covers. * * @return Horde_Db_Adapter_Base_Index An index object. */ public function makeIndex($table, $name, $primary, $unique, $columns) { return new Horde_Db_Adapter_Base_Index($table, $name, $primary, $unique, $columns); } /** * Factory for Table objects. * * @return Horde_Db_Adapter_Base_Table A table object. */ public function makeTable($name, $primaryKey, $columns, $indexes) { return new Horde_Db_Adapter_Base_Table($name, $primaryKey, $columns, $indexes); } /** * Factory for TableDefinition objects. * * @return Horde_Db_Adapter_Base_TableDefinition A table definition object. */ public function makeTableDefinition($name, $base, $options = array()) { return new Horde_Db_Adapter_Base_TableDefinition($name, $base, $options); } /*########################################################################## # Object composition ##########################################################################*/ /** * Delegates calls to the adapter object. * * @param string $method A method name. * @param array $args Method parameters. * * @return mixed The method call result. * @throws BadMethodCallException if method doesn't exist in the adapter. */ public function __call($method, $args) { if (isset($this->_adapterMethods[$method])) { return call_user_func_array(array($this->_adapter, $method), $args); } throw new BadMethodCallException('Call to undeclared method "' . $method . '"'); } /** * Delegates access to $_cache and $_logger to the adapter object. * * @param string $key Property name. Only '_cache' and '_logger' are * supported. * * @return object The request property object. */ public function __get($key) { if ($key == '_cache' || $key == '_logger') { $getter = 'get' . ucfirst(substr($key, 1)); return $this->_adapter->$getter(); } } /*########################################################################## # Quoting ##########################################################################*/ /** * Quotes the column value to help prevent SQL injection attacks. * * This method makes educated guesses on the scalar type based on the * passed value. Make sure to correctly cast the value and/or pass the * $column parameter to get the best results. * * @param mixed $value The scalar value to quote, a Horde_Db_Value, * Horde_Date, or DateTime instance, or an object * implementing quotedId(). * @param object $column An object implementing getType(). * * @return string The correctly quoted value. */ public function quote($value, $column = null) { if (is_object($value) && is_callable(array($value, 'quotedId'))) { return $value->quotedId(); } if ($value instanceof Horde_Db_Value) { return $value->quote($this->_adapter); } $type = isset($column) ? $column->getType() : null; if (is_null($value)) { return 'NULL'; } elseif ($value === true) { return $type == 'integer' ? '1' : $this->quoteTrue(); } elseif ($value === false) { return $type == 'integer' ? '0' : $this->quoteFalse(); } elseif (is_float($value)) { return sprintf('%F', $value); } elseif (is_int($value)) { return $value; } elseif ($value instanceof DateTime || $value instanceof Horde_Date) { return $this->_adapter->quoteString($type == 'integer' ? $value->format('U') : $value->format('Y-m-d H:i:s')); } elseif ($type == 'integer') { return (int)$value; } elseif ($type == 'float') { return sprintf('%F', $value); } else { return $this->_adapter->quoteString($value); } } /** * Quotes a string, escaping any ' (single quote) and \ (backslash) * characters. * * @param string $string A string to escape. * * @return string The escaped and quoted string. */ public function quoteString($string) { return "'" . str_replace(array('\\', '\''), array('\\\\', '\\\''), $string) . "'"; } /** * Returns a quoted form of the column name. * * @param string $name A column name. * * @return string The quoted column name. */ abstract public function quoteColumnName($name); /** * Returns a quoted form of the table name. * * Defaults to column name quoting. * * @param string $name A table name. * * @return string The quoted table name. */ public function quoteTableName($name) { return $this->quoteColumnName($name); } /** * Returns a quoted boolean true. * * @return string The quoted boolean true. */ public function quoteTrue() { return "'t'"; } /** * Returns a quoted boolean false. * * @return string The quoted boolean false. */ public function quoteFalse() { return "'f'"; } /** * Returns a quoted date value. * * @param mixed A date value that can be casted to string. * * @return string The quoted date value. */ public function quoteDate($value) { return $this->quoteString((string)$value); } /** * Returns a quoted binary value. * * @param mixed A binary value. * * @return string The quoted binary value. */ public function quoteBinary($value) { return $this->quoteString($value); } /*########################################################################## # Schema Statements ##########################################################################*/ /** * Returns a hash of mappings from the abstract data types to the native * database types. * * See TableDefinition::column() for details on the recognized abstract * data types. * * @see TableDefinition::column() * * @return array A database type map. */ public function nativeDatabaseTypes() { return array(); } /** * Returns the maximum length a table alias can have. * * @return integer The maximum table alias length. */ public function tableAliasLength() { return 255; } /** * Converts a table name into a suitable table alias. * * @param string $tableName A table name. * * @return string A possible alias name for the table. */ public function tableAliasFor($tableName) { $alias = substr($tableName, 0, $this->tableAliasLength()); return str_replace('.', '_', $alias); } /** * Returns a list of all tables of the current database. * * @return array A table list. */ abstract public function tables(); /** * Returns a Horde_Db_Adapter_Base_Table object for a table. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return Horde_Db_Adapter_Base_Table A table object. */ public function table($tableName, $name = null) { return $this->makeTable( $tableName, $this->primaryKey($tableName), $this->columns($tableName, $name), $this->indexes($tableName, $name) ); } /** * Returns a table's primary key. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return Horde_Db_Adapter_Base_Index The primary key index object. */ abstract public function primaryKey($tableName, $name = null); /** * Returns a list of tables indexes. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return array A list of Horde_Db_Adapter_Base_Index objects. */ abstract public function indexes($tableName, $name = null); /** * Returns a list of table columns. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return array A list of Horde_Db_Adapter_Base_Column objects. */ abstract public function columns($tableName, $name = null); /** * Creates a new table. * * The $options hash can include the following keys: * - autoincrementKey (string|array): * The name of the autoincrementing primary key, if one is to be added * automatically. Defaults to "id". * - options (array): * Any extra options you want appended to the table definition. * - temporary (boolean): * Make a temporary table. * - force (boolean): * Set to true or false to drop the table before creating it. * Defaults to false. * * Examples: * * // Add a backend specific option to the generated SQL (MySQL) * $schema->createTable('suppliers', array('options' => 'ENGINE=InnoDB DEFAULT CHARSET=utf8'))); * * generates: *
     *  CREATE TABLE suppliers (
     *    id int(10) UNSIGNED  NOT NULL AUTO_INCREMENT PRIMARY KEY
     *  ) ENGINE=InnoDB DEFAULT CHARSET=utf8
     * 
* * * // Rename the primary key column * $table = $schema->createTable('objects', array('autoincrementKey' => 'guid')); * $table->column('name', 'string', array('limit' => 80)); * $table->end(); * * generates: *
     *  CREATE TABLE objects (
     *    guid int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
     *    name varchar(80)
     *  )
     * 
* * * // Do not add a primary key column, use fluent interface, use type * // method. * $schema->createTable('categories_suppliers', array('autoincrementKey' => false)) * ->column('category_id', 'integer') * ->integer('supplier_id') * ->end(); * * generates: *
     *  CREATE TABLE categories_suppliers (
     *    category_id int(11),
     *    supplier_id int(11)
     *  )
     * 
* * See also Horde_Db_Adapter_Base_TableDefinition::column() for details on * how to create columns. * * @param string $name A table name. * @param array $options A list of table options, see the method * description. * * @return Horde_Db_Adapter_Base_TableDefinition The definition of the * created table. */ public function createTable($name, $options = array()) { $tableDefinition = $this->makeTableDefinition($name, $this, $options); if (isset($options['autoincrementKey'])) { if ($options['autoincrementKey'] === true || $options['autoincrementKey'] === 'true' || $options['autoincrementKey'] === 't' || $options['autoincrementKey'] === 1 || $options['autoincrementKey'] === '1') { $pk = 'id'; } elseif ($options['autoincrementKey'] === false || $options['autoincrementKey'] === 'false' || $options['autoincrementKey'] === 'f' || $options['autoincrementKey'] === 0 || $options['autoincrementKey'] === '0') { $pk = false; } else { $pk = $options['autoincrementKey']; } } else { $pk = 'id'; } if ($pk != false) { $tableDefinition->primaryKey($pk); } return $tableDefinition; } /** * Finishes and executes table creation. * * @param string|Horde_Db_Adapter_Base_TableDefinition $name * A table name or object. * @param array $options * A list of options. See createTable(). */ public function endTable($name, $options = array()) { if ($name instanceof Horde_Db_Adapter_Base_TableDefinition) { $tableDefinition = $name; $options = array_merge($tableDefinition->getOptions(), $options); } else { $tableDefinition = $this->createTable($name, $options); } // Drop previous table. if (isset($options['force'])) { $this->dropTable($tableDefinition->getName(), $options); } $temp = !empty($options['temporary']) ? 'TEMPORARY' : null; $opts = !empty($options['options']) ? $options['options'] : null; $sql = sprintf("CREATE %s TABLE %s (\n%s\n) %s", $temp, $this->quoteTableName($tableDefinition->getName()), $tableDefinition->toSql(), $opts); return $this->execute($sql); } /** * Renames a table. * * @param string $name A table name. * @param string $newName The new table name. */ abstract public function renameTable($name, $newName); /** * Drops a table from the database. * * @param string $name A table name. */ public function dropTable($name) { $this->_clearTableCache($name); return $this->execute('DROP TABLE ' . $this->quoteTableName($name)); } /** * Adds a new column to a table. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $type A data type. * @param array $options Column options. See * Horde_Db_Adapter_Base_TableDefinition#column() * for details. */ public function addColumn($tableName, $columnName, $type, $options = array()) { $this->_clearTableCache($tableName); $options = array_merge( array('limit' => null, 'precision' => null, 'scale' => null, 'unsigned' => null), $options); $sql = sprintf('ALTER TABLE %s ADD %s %s', $this->quoteTableName($tableName), $this->quoteColumnName($columnName), $this->typeToSql($type, $options['limit'], $options['precision'], $options['scale'], $options['unsigned'])); $sql = $this->addColumnOptions($sql, $options); return $this->execute($sql); } /** * Removes a column from a table. * * @param string $tableName A table name. * @param string $columnName A column name. */ public function removeColumn($tableName, $columnName) { $this->_clearTableCache($tableName); $sql = sprintf('ALTER TABLE %s DROP %s', $this->quoteTableName($tableName), $this->quoteColumnName($columnName)); return $this->execute($sql); } /** * Changes an existing column's definition. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $type A data type. * @param array $options Column options. See * Horde_Db_Adapter_Base_TableDefinition#column() * for details. */ abstract public function changeColumn($tableName, $columnName, $type, $options = array()); /** * Sets a new default value for a column. * * If you want to set the default value to NULL, you are out of luck. You * need to execute the apppropriate SQL statement yourself. * * @param string $tableName A table name. * @param string $columnName A column name. * @param mixed $default The new default value. */ abstract public function changeColumnDefault($tableName, $columnName, $default); /** * Renames a column. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $newColumnName The new column name. */ abstract public function renameColumn($tableName, $columnName, $newColumnName); /** * Adds a primary key to a table. * * @param string $tableName A table name. * @param string|array $columnName One or more column names. * * @throws Horde_Db_Exception */ public function addPrimaryKey($tableName, $columns) { $this->_clearTableCache($tableName); $columns = (array)$columns; $sql = sprintf('ALTER TABLE %s ADD PRIMARY KEY (%s)', $this->quoteTableName($tableName), implode(', ', $columns)); return $this->execute($sql); } /** * Removes a primary key from a table. * * @param string $tableName A table name. * * @throws Horde_Db_Exception */ abstract public function removePrimaryKey($tableName); /** * Adds a new index to a table. * * The index will be named after the table and the first column names, * unless you pass 'name' as an option. * * When creating an index on multiple columns, the first column is used as * a name for the index. For example, when you specify an index on two * columns 'first' and 'last', the DBMS creates an index for both columns * as well as an index for the first colum 'first'. Using just the first * name for this index makes sense, because you will never have to create a * singular index with this name. * * Examples: * * Creating a simple index * * $schema->addIndex('suppliers', 'name'); * * generates * * CREATE INDEX suppliers_name_index ON suppliers(name) * * * Creating a unique index * * $schema->addIndex('accounts', * array('branch_id', 'party_id'), * array('unique' => true)); * * generates * * CREATE UNIQUE INDEX accounts_branch_id_index ON accounts(branch_id, party_id) * * * Creating a named index * * $schema->addIndex('accounts', * array('branch_id', 'party_id'), * array('unique' => true, 'name' => 'by_branch_party')); * * generates * * CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id) * * * @param string $tableName A table name. * @param string|array $columnName One or more column names. * @param array $options Index options: * - name: (string) the index name. * - unique: (boolean) create a unique * index? */ public function addIndex($tableName, $columnName, $options = array()) { $this->_clearTableCache($tableName); $columnNames = (array)$columnName; $indexName = empty($options['name']) ? $this->indexName($tableName, array('column' => $columnNames)) : $options['name']; foreach ($columnNames as &$colName) { $colName = $this->quoteColumnName($colName); } $sql = sprintf('CREATE %s INDEX %s ON %s (%s)', empty($options['unique']) ? null : 'UNIQUE', $this->quoteColumnName($indexName), $this->quoteTableName($tableName), implode(', ', $columnNames)); return $this->execute($sql); } /** * Removes an index from a table. * * Examples: * * Remove the suppliers_name_index in the suppliers table: * * $schema->removeIndex('suppliers', 'name'); * * * Remove the index named accounts_branch_id in the accounts table: * * $schema->removeIndex('accounts', array('column' => 'branch_id')); * * * Remove the index named by_branch_party in the accounts table: * * $schema->removeIndex('accounts', array('name' => 'by_branch_party')); * * * You can remove an index on multiple columns by specifying the first * column: * * $schema->addIndex('accounts', array('username', 'password')) * $schema->removeIndex('accounts', 'username'); * * * @param string $tableName A table name. * @param string|array $options Either a column name or index options: * - name: (string) the index name. * - column: (string|array) column name(s). */ public function removeIndex($tableName, $options = array()) { $this->_clearTableCache($tableName); $index = $this->indexName($tableName, $options); $sql = sprintf('DROP INDEX %s ON %s', $this->quoteColumnName($index), $this->quoteTableName($tableName)); return $this->execute($sql); } /** * Builds the name for an index. * * @param string $tableName A table name. * @param string|array $options Either a column name or index options: * - column: (string|array) column name(s). * - name: (string) the index name to fall * back to if no column names specified. */ public function indexName($tableName, $options = array()) { if (!is_array($options)) { $options = array('column' => $options); } if (isset($options['column'])) { $columns = (array)$options['column']; return "index_{$tableName}_on_" . implode('_and_', $columns); } if (isset($options['name'])) { return $options['name']; } throw new Horde_Db_Exception('You must specify the index name'); } /** * Recreates, i.e. drops then creates a database. * * @param string $name A database name. */ public function recreateDatabase($name) { $this->dropDatabase($name); return $this->createDatabase($name); } /** * Creates a database. * * @param string $name A database name. * @param array $options Database options. */ abstract public function createDatabase($name, $options = array()); /** * Drops a database. * * @param string $name A database name. */ abstract public function dropDatabase($name); /** * Returns the name of the currently selected database. * * @return string The database name. */ abstract public function currentDatabase(); /** * Generates the SQL definition for a column type. * * @param string $type A column type. * @param integer $limit Maximum column length (non decimal type only) * @param integer $precision The number precision (decimal type only). * @param integer $scale The number scaling (decimal columns only). * @param boolean $unsigned Whether the column is an unsigned number * (non decimal columns only). * * @return string The SQL definition. If $type is not one of the * internally supported types, $type is returned unchanged. */ public function typeToSql($type, $limit = null, $precision = null, $scale = null, $unsigned = null) { $natives = $this->nativeDatabaseTypes(); $native = isset($natives[$type]) ? $natives[$type] : null; if (empty($native)) { return $type; } $sql = is_array($native) ? $native['name'] : $native; if ($type == 'decimal') { $nativePrec = isset($native['precision']) ? $native['precision'] : null; $nativeScale = isset($native['scale']) ? $native['scale'] : null; $precision = !empty($precision) ? $precision : $nativePrec; $scale = !empty($scale) ? $scale : $nativeScale; if ($precision) { $sql .= $scale ? "($precision, $scale)" : "($precision)"; } } else { $nativeLimit = is_array($native) ? $native['limit'] : null; // If there is no explicit limit, adjust $nativeLimit for unsigned // integers. if (!empty($unsigned) && empty($limit) && is_integer($nativeLimit)) { $nativeLimit--; } if ($limit = !empty($limit) ? $limit : $nativeLimit) { $sql .= "($limit)"; } } return $sql; } /** * Adds default/null options to column SQL definitions. * * @param string $sql Existing SQL definition for a column. * @param array $options Column options: * - null: (boolean) Whether to allow NULL values. * - default: (mixed) Default column value. * - autoincrement: (boolean) Whether the column is * an autoincrement column. Driver depedendent. * * @return string The manipulated SQL definition. */ public function addColumnOptions($sql, $options) { /* 'autoincrement' is not handled here - it varies too much between * DBs. Do autoincrement-specific handling in the driver. */ if (isset($options['null']) && $options['null'] === false) { $sql .= ' NOT NULL'; } if (isset($options['default'])) { $default = $options['default']; $column = isset($options['column']) ? $options['column'] : null; $sql .= ' DEFAULT ' . $this->quote($default, $column); } return $sql; } /** * Generates a DISTINCT clause for SELECT queries. * * * $connection->distinct('posts.id', 'posts.created_at DESC') * * * @param string $columns A column list. * @param string $orderBy An ORDER clause. * * @return string The generated DISTINCT clause. */ public function distinct($columns, $orderBy = null) { return 'DISTINCT ' . $columns; } /** * Adds an ORDER BY clause to an existing query. * * @param string $sql An SQL query to manipulate. * @param array $options Options: * - order: Order column an direction. * * @return string The manipulated SQL query. */ public function addOrderByForAssocLimiting($sql, $options) { return $sql . 'ORDER BY ' . $options['order']; } /** * Generates an INTERVAL clause for SELECT queries. * * @deprecated since version 1.2.0. This function does not work with SQLite * as a backend so you should avoid using it. Use "modifyDate()" instead. * * @param string $interval The interval. * @param string $precision The precision. * * @return string The generated INTERVAL clause. */ public function interval($interval, $precision) { return 'INTERVAL ' . $precision . ' ' . $interval; } /** * Generates a modified date for SELECT queries. * * @param string $reference The reference date - this is a column * referenced in the SELECT. * @param string $operator Add or subtract time? (+/-) * @param integer $amount The shift amount (number of days if $interval * is DAY, etc). * @param string $interval The interval (SECOND, MINUTE, HOUR, DAY, * MONTH, YEAR). * * @return string The generated INTERVAL clause. */ public function modifyDate($reference, $operator, $amount, $interval) { if (!is_int($amount)) { throw new InvalidArgumentException('$amount parameter must be an integer'); } return sprintf('%s %s INTERVAL \'%s\' %s', $reference, $operator, $amount, $interval); } /** * Returns an expression using the specified operator. * * @param string $lhs The column or expression to test. * @param string $op The operator. * @param string $rhs The comparison value. * @param boolean $bind If true, the method returns the query and a list * of values suitable for binding as an array. * @param array $params Any additional parameters for the operator. * * @return string|array The SQL test fragment, or an array containing the * query and a list of values if $bind is true. */ public function buildClause($lhs, $op, $rhs, $bind = false, $params = array()) { $lhs = $this->_escapePrepare($lhs); switch ($op) { case '|': case '&': if ($bind) { return array($lhs . ' ' . $op . ' ?', array((int)$rhs)); } return $lhs . ' ' . $op . ' ' . (int)$rhs; case '~': if ($bind) { return array($lhs . ' ' . $op . ' ?', array($rhs)); } return $lhs . ' ' . $op . ' ' . $rhs; case 'IN': if ($bind) { if (is_array($rhs)) { return array($lhs . ' IN (?' . str_repeat(', ?', count($rhs) - 1) . ')', $rhs); } /* We need to bind each member of the IN clause separately to * ensure proper quoting. */ if (substr($rhs, 0, 1) == '(') { $rhs = substr($rhs, 1); } if (substr($rhs, -1) == ')') { $rhs = substr($rhs, 0, -1); } $ids = preg_split('/\s*,\s*/', $rhs); return array($lhs . ' IN (?' . str_repeat(', ?', count($ids) - 1) . ')', $ids); } if (is_array($rhs)) { return $lhs . ' IN ' . implode(', ', $rhs); } return $lhs . ' IN ' . $rhs; case 'LIKE': $query = 'LOWER(%s) LIKE LOWER(%s)'; if ($bind) { if (empty($params['begin'])) { return array(sprintf($query, $lhs, '?'), array('%' . $rhs . '%')); } return array(sprintf('(' . $query . ' OR ' . $query . ')', $lhs, '?', $lhs, '?'), array($rhs . '%', '% ' . $rhs . '%')); } if (empty($params['begin'])) { return sprintf($query, $lhs, $this->_escapePrepare($this->quote('%' . $rhs . '%'))); } return sprintf('(' . $query . ' OR ' . $query . ')', $lhs, $this->_escapePrepare($this->quote($rhs . '%')), $lhs, $this->_escapePrepare($this->quote('% ' . $rhs . '%'))); default: if ($bind) { return array($lhs . ' ' . $this->_escapePrepare($op) . ' ?', array($rhs)); } return $lhs . ' ' . $this->_escapePrepare($op . ' ' . $this->quote($rhs)); } } /** * Escapes all characters in a string that are placeholders for * prepare/execute methods. * * @param string $query A string to escape. * * @return string The correctly escaped string. */ protected function _escapePrepare($query) { return preg_replace('/[?!&]/', '\\\\$0', $query); } /*########################################################################## # Protected ##########################################################################*/ /** * Clears the cache for tables when altering them. * * @param string $tableName A table name. */ protected function _clearTableCache($tableName) { $this->_cache->set('tables/columns/' . $tableName, ''); $this->_cache->set('tables/indexes/' . $tableName, ''); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Base/Table.php0000664000175000017500000001077712205351473017433 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Base_Table implements ArrayAccess, IteratorAggregate { /** * The table's name. * * @var string */ protected $_name; protected $_primaryKey; protected $_columns; protected $_indexes; /*########################################################################## # Construct/Destruct ##########################################################################*/ /** * Constructor. * * @param string $name The table's name. */ public function __construct($name, $primaryKey, $columns, $indexes) { $this->_name = $name; $this->_primaryKey = $primaryKey; $this->_columns = $columns; $this->_indexes = $indexes; } /*########################################################################## # Accessor ##########################################################################*/ /** * @return string */ public function getName() { return $this->_name; } /** * @return mixed */ public function getPrimaryKey() { return $this->_primaryKey; } /** * @return array */ public function getColumns() { return $this->_columns; } /** * @return Horde_Db_Adapter_Base_Column */ public function getColumn($column) { return isset($this->_columns[$column]) ? $this->_columns[$column] : null; } /** * @return array */ public function getColumnNames() { $names = array(); foreach ($this->_columns as $column) { $names[] = $column->getName(); } return $names; } /** * @return array */ public function getIndexes() { return $this->_indexes; } /** * @return array */ public function getIndexNames() { $names = array(); foreach ($this->_indexes as $index) { $names[] = $index->getName(); } return $names; } /*########################################################################## # Object composition ##########################################################################*/ public function __get($key) { return $this->getColumn($key); } public function __isset($key) { return isset($this->_columns[$key]); } /*########################################################################## # ArrayAccess ##########################################################################*/ /** * ArrayAccess: Check if the given offset exists * * @param int $offset * @return boolean */ public function offsetExists($offset) { return isset($this->_columns[$offset]); } /** * ArrayAccess: Return the value for the given offset. * * @param int $offset * @return object {@link {@Horde_Db_Adapter_Base_ColumnDefinition} */ public function offsetGet($offset) { return $this->getColumn($offset); } /** * ArrayAccess: Set value for given offset * * @param int $offset * @param mixed $value */ public function offsetSet($offset, $value) { } /** * ArrayAccess: remove element * * @param int $offset */ public function offsetUnset($offset) { } /*########################################################################## # IteratorAggregate ##########################################################################*/ public function getIterator() { return new ArrayIterator($this->_columns); } /*########################################################################## # Protected ##########################################################################*/ } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Base/TableDefinition.php0000664000175000017500000002267612205351473021445 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Base_TableDefinition implements ArrayAccess, IteratorAggregate { protected $_name = null; protected $_base = null; protected $_options = null; protected $_columns = null; protected $_primaryKey = null; protected $_columntypes = array('string', 'text', 'integer', 'float', 'datetime', 'timestamp', 'time', 'date', 'binary', 'boolean'); /** * Constructor. * * @param string $name * @param Horde_Db_Adapter_Base_Schema $base * @param array $options */ public function __construct($name, $base, $options = array()) { $this->_name = $name; $this->_base = $base; $this->_options = $options; $this->_columns = array(); } /** * @return string */ public function getName() { return $this->_name; } /** * @return array */ public function getOptions() { return $this->_options; } /** * @param string $name */ public function primaryKey($name) { if (is_scalar($name) && $name !== false) { $natives = $this->_native(); $this->column($name, $natives['autoincrementKey']); } $this->_primaryKey = $name; } /** * Adds a new column to the table definition. * * Examples: * * // Assuming $def is an instance of Horde_Db_Adapter_Base_TableDefinition * * $def->column('granted', 'boolean'); * // => granted BOOLEAN * * $def->column('picture', 'binary', 'limit' => 4096); * // => picture BLOB(4096) * * $def->column('sales_stage', 'string', array('limit' => 20, 'default' => 'new', 'null' => false)); * // => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL * * * @param string $type Column type, one of: * autoincrementKey, string, text, integer, float, * datetime, timestamp, time, date, binary, boolean. * @param array $options Column options: * - limit: (integer) Maximum column length (string, * text, binary or integer columns only) * - default: (mixed) The column's default value. * You cannot explicitly set the default value to * NULL. Simply leave off this option if you want * a NULL default value. * - null: (boolean) Whether NULL values are allowed * in the column. * - precision: (integer) The number precision * (float columns only). * - scale: (integer) The number scaling (float * columns only). * - unsigned: (boolean) Whether the column is an * unsigned number (integer columns only). * - autoincrement: (boolean) Whether the column is * an autoincrement column. Restrictions are * RDMS specific. * * @return Horde_Db_Adapter_Base_TableDefinition This object. */ public function column($name, $type, $options = array()) { $options = array_merge( array('limit' => null, 'precision' => null, 'scale' => null, 'unsigned' => null, 'default' => null, 'null' => null, 'autoincrement' => null), $options); $column = $this->_base->makeColumnDefinition($this->_base, $name, $type); $column->setLimit($options['limit']); $column->setPrecision($options['precision']); $column->setScale($options['scale']); $column->setUnsigned($options['unsigned']); $column->setDefault($options['default']); $column->setNull($options['null']); $column->setAutoIncrement($options['autoincrement']); $this[$name] ? $this[$name] = $column : $this->_columns[] = $column; return $this; } /** * Adds created_at and updated_at columns to the table. */ public function timestamps() { return $this->column('created_at', 'datetime') ->column('updated_at', 'datetime'); } /** * Add one or several references to foreign keys * * This method returns self. */ public function belongsTo($columns) { if (!is_array($columns)) { $columns = array($columns); } foreach ($columns as $col) { $this->column($col . '_id', 'integer'); } return $this; } /** * Alias for the belongsTo() method * * This method returns self. */ public function references($columns) { return $this->belongsTo($columns); } /** * Use __call to provide shorthand column creation ($this->integer(), etc.) */ public function __call($method, $arguments) { if (!in_array($method, $this->_columntypes)) { throw new BadMethodCallException('Call to undeclared method "' . $method . '"'); } if (count($arguments) > 0 && count($arguments) < 3) { return $this->column($arguments[0], $method, isset($arguments[1]) ? $arguments[1] : array()); } throw new BadMethodCallException('Method "'.$method.'" takes two arguments'); } /** * Wrap up table creation block & create the table */ public function end() { return $this->_base->endTable($this); } /** * Returns a String whose contents are the column definitions * concatenated together. This string can then be pre and appended to * to generate the final SQL to create the table. * * @return string */ public function toSql() { $cols = array(); foreach ($this->_columns as $col) { $cols[] = $col->toSql(); } $sql = ' ' . implode(", \n ", $cols); // Specify composite primary keys as well if (is_array($this->_primaryKey)) { $pk = array(); foreach ($this->_primaryKey as $pkColumn) { $pk[] = $this->_base->quoteColumnName($pkColumn); } $sql .= ", \n PRIMARY KEY(" . implode(', ', $pk) . ')'; } return $sql; } public function __toString() { return $this->toSql(); } /*########################################################################## # ArrayAccess ##########################################################################*/ /** * ArrayAccess: Check if the given offset exists * * @param int $offset * @return boolean */ public function offsetExists($offset) { foreach ($this->_columns as $column) { if ($column->getName() == $offset) return true; } return false; } /** * ArrayAccess: Return the value for the given offset. * * @param int $offset * @return object {@link {@Horde_Db_Adapter_Base_ColumnDefinition} */ public function offsetGet($offset) { if (!$this->offsetExists($offset)) { return null; } foreach ($this->_columns as $column) { if ($column->getName() == $offset) { return $column; } } } /** * ArrayAccess: Set value for given offset * * @param int $offset * @param mixed $value */ public function offsetSet($offset, $value) { foreach ($this->_columns as $key=>$column) { if ($column->getName() == $offset) { $this->_columns[$key] = $value; } } } /** * ArrayAccess: remove element * * @param int $offset */ public function offsetUnset($offset) { foreach ($this->_columns as $key=>$column) { if ($column->getName() == $offset) { unset($this->_columns[$key]); } } } /*########################################################################## # IteratorAggregate ##########################################################################*/ public function getIterator() { return new ArrayIterator($this->_columns); } /*########################################################################## # Protected ##########################################################################*/ /** * Get the types */ protected function _native() { return $this->_base->nativeDatabaseTypes(); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Mysql/Column.php0000664000175000017500000000454212205351473020065 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Mysql_Column extends Horde_Db_Adapter_Base_Column { /** * @var array */ protected static $_hasEmptyStringDefault = array('binary', 'string', 'text'); /** * @var string */ protected $_originalDefault = null; /** * Construct * @param string $name * @param string $default * @param string $sqlType * @param boolean $null */ public function __construct($name, $default, $sqlType=null, $null=true) { $this->_originalDefault = $default; parent::__construct($name, $default, $sqlType, $null); if ($this->_isMissingDefaultForgedAsEmptyString()) { $this->_default = null; } } /** * @param string $fieldType * @return string */ protected function _simplifiedType($fieldType) { if (strpos(strtolower($fieldType), 'tinyint(1)') !== false) { return 'boolean'; } elseif (preg_match('/enum/i', $fieldType)) { return 'string'; } return parent::_simplifiedType($fieldType); } /** * MySQL misreports NOT NULL column default when none is given. * We can't detect this for columns which may have a legitimate '' * default (string, text, binary) but we can for others (integer, * datetime, boolean, and the rest). * * Test whether the column has default '', is not null, and is not * a type allowing default ''. * * @return boolean */ protected function _isMissingDefaultForgedAsEmptyString() { return !$this->_null && $this->_originalDefault == '' && !in_array($this->_type, self::$_hasEmptyStringDefault); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Mysql/Result.php0000664000175000017500000001235612205351473020110 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Mysql_Result implements Iterator { /** * @var Horde_Db_Adapter */ protected $_adapter; /** * @var string */ protected $_sql; /** * @var mixed */ protected $_arg1; /** * @var string */ protected $_arg2; /** * Result resource * @var resource */ protected $_result; /** * Current row * @var array */ protected $_current; /** * Current offset * @var integer */ protected $_index; /** * Are we at the end of the result? * @var boolean */ protected $_eof; /** * Which kind of keys to use for results. */ protected $_fetchMode = MYSQL_ASSOC; /** * Constructor * * @param Horde_Db_Adapter $adapter * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function __construct($adapter, $sql, $arg1 = null, $arg2 = null) { $this->_adapter = $adapter; $this->_sql = $sql; $this->_arg1 = $arg1; $this->_arg2 = $arg2; } /** * Destructor - release any resources. */ public function __destruct() { if ($this->_result) { unset($this->_result); } } /** * Implementation of the rewind() method for iterator. */ public function rewind() { if ($this->_result) { unset($this->_result); } $this->_current = null; $this->_index = null; $this->_eof = true; $this->_result = $this->_adapter->execute($this->_sql, $this->_arg1, $this->_arg2); $this->next(); } /** * Implementation of the current() method for iterator. * * @return mixed The current row, or null if no rows. */ public function current() { if (is_null($this->_result)) { $this->rewind(); } return $this->_current; } /** * Implementation of the key() method for iterator. * * @return mixed The current row number (starts at 0), or NULL if no rows */ public function key() { if (is_null($this->_result)) { $this->rewind(); } return $this->_index; } /** * Implementation of the next() method. * * @return array|null The next row in the resultset or null if there are no * more results. */ public function next() { if (is_null($this->_result)) { $this->rewind(); } if ($this->_result) { $row = mysql_fetch_array($this->_result, $this->_fetchMode); if (!$row) { $this->_eof = true; } else { $this->_eof = false; if (is_null($this->_index)) { $this->_index = 0; } else { ++$this->_index; } $this->_current = $row; } } return $this->_current; } /** * Returns the current row and advances the recordset one row. * * @param integer $fetchmode The default fetch mode for this result. One * of the Horde_Db::FETCH_* constants. */ public function fetch($fetchmode = Horde_Db::FETCH_ASSOC) { if (!$this->valid()) { return null; } $this->setFetchMode($fetchmode); $row = $this->current(); $this->next(); return $row; } /** * Implementation of the valid() method for iterator * * @return boolean Whether the iteration is valid */ public function valid() { if (is_null($this->_result)) { $this->rewind(); } return !$this->_eof; } /** * Sets the default fetch mode for this result. * * @param integer $fetchmode One of the Horde_Db::FETCH_* constants. */ public function setFetchMode($fetchmode) { $map = array(Horde_Db::FETCH_ASSOC => MYSQL_ASSOC, Horde_Db::FETCH_NUM => MYSQL_NUM, Horde_Db::FETCH_BOTH => MYSQL_BOTH); $this->_fetchMode = $map[$fetchmode]; } /** * Returns the number of columns in the result set * * @return integer Number of columns. */ public function columnCount() { if (is_null($this->_result)) { $this->rewind(); } return mysql_num_fields($this->_result); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Mysql/Schema.php0000664000175000017500000005316312205351473020033 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Mysql_Schema extends Horde_Db_Adapter_Base_Schema { /*########################################################################## # Object factories ##########################################################################*/ /** * Factory for Column objects. * * @param string $name The column's name, such as "supplier_id" in * "supplier_id int(11)". * @param string $default The type-casted default value, such as "new" in * "sales_stage varchar(20) default 'new'". * @param string $sqlType Used to extract the column's type, length and * signed status, if necessary. For example * "varchar" and "60" in "company_name varchar(60)" * or "unsigned => true" in "int(10) UNSIGNED". * @param boolean $null Whether this column allows NULL values. * * @return Horde_Db_Adapter_Mysql_Column A column object. */ public function makeColumn($name, $default, $sqlType = null, $null = true) { return new Horde_Db_Adapter_Mysql_Column($name, $default, $sqlType, $null); } /*########################################################################## # Quoting ##########################################################################*/ /** * Returns a quoted form of the column name. * * @param string $name A column name. * * @return string The quoted column name. */ public function quoteColumnName($name) { return '`' . str_replace('`', '``', $name) . '`'; } /** * Returns a quoted form of the table name. * * Defaults to column name quoting. * * @param string $name A table name. * * @return string The quoted table name. */ public function quoteTableName($name) { return str_replace('.', '`.`', $this->quoteColumnName($name)); } /** * Returns a quoted boolean true. * * @return string The quoted boolean true. */ public function quoteTrue() { return '1'; } /** * Returns a quoted boolean false. * * @return string The quoted boolean false. */ public function quoteFalse() { return '0'; } /*########################################################################## # Schema Statements ##########################################################################*/ /** * Returns a hash of mappings from the abstract data types to the native * database types. * * See TableDefinition::column() for details on the recognized abstract * data types. * * @see TableDefinition::column() * * @return array A database type map. */ public function nativeDatabaseTypes() { return array( 'autoincrementKey' => 'int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', 'string' => array('name' => 'varchar', 'limit' => 255), 'text' => array('name' => 'text', 'limit' => null), 'mediumtext' => array('name' => 'mediumtext', 'limit' => null), 'longtext' => array('name' => 'longtext', 'limit' => null), 'integer' => array('name' => 'int', 'limit' => 11), 'float' => array('name' => 'float', 'limit' => null), 'decimal' => array('name' => 'decimal', 'limit' => null), 'datetime' => array('name' => 'datetime', 'limit' => null), 'timestamp' => array('name' => 'datetime', 'limit' => null), 'time' => array('name' => 'time', 'limit' => null), 'date' => array('name' => 'date', 'limit' => null), 'binary' => array('name' => 'longblob', 'limit' => null), 'boolean' => array('name' => 'tinyint', 'limit' => 1), ); } /** * Returns a list of all tables of the current database. * * @return array A table list. */ public function tables() { return $this->selectValues('SHOW TABLES'); } /** * Returns a table's primary key. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return Horde_Db_Adapter_Base_Index The primary key index object. */ public function primaryKey($tableName, $name = null) { // Share the column cache with the columns() method $rows = @unserialize($this->_cache->get("tables/columns/$tableName", 0)); if (!$rows) { $rows = $this->selectAll( 'SHOW FIELDS FROM ' . $this->quoteTableName($tableName), $name); $this->_cache->set("tables/columns/$tableName", serialize($rows)); } $pk = $this->makeIndex($tableName, 'PRIMARY', true, true, array()); foreach ($rows as $row) { if ($row['Key'] == 'PRI') { $pk->columns[] = $row['Field']; } } return $pk; } /** * Returns a list of tables indexes. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return array A list of Horde_Db_Adapter_Base_Index objects. */ public function indexes($tableName, $name=null) { $indexes = @unserialize($this->_cache->get("tables/indexes/$tableName", 0)); if (!$indexes) { $indexes = array(); $currentIndex = null; foreach ($this->select('SHOW KEYS FROM ' . $this->quoteTableName($tableName)) as $row) { if ($currentIndex != $row['Key_name']) { if ($row['Key_name'] == 'PRIMARY') { continue; } $currentIndex = $row['Key_name']; $indexes[] = $this->makeIndex( $tableName, $row['Key_name'], false, $row['Non_unique'] == '0', array()); } $indexes[count($indexes) - 1]->columns[] = $row['Column_name']; } $this->_cache->set("tables/indexes/$tableName", serialize($indexes)); } return $indexes; } /** * Returns a list of table columns. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return array A list of Horde_Db_Adapter_Base_Column objects. */ public function columns($tableName, $name=null) { $rows = @unserialize($this->_cache->get("tables/columns/$tableName", 0)); if (!$rows) { $rows = $this->selectAll('SHOW FIELDS FROM ' . $this->quoteTableName($tableName), $name); $this->_cache->set("tables/columns/$tableName", serialize($rows)); } // Create columns from rows. $columns = array(); foreach ($rows as $row) { $columns[$row['Field']] = $this->makeColumn( $row['Field'], $row['Default'], $row['Type'], $row['Null'] == 'YES'); } return $columns; } /** * Finishes and executes table creation. * * @param string|Horde_Db_Adapter_Base_TableDefinition $name * A table name or object. * @param array $options * A list of options. See createTable(). */ public function endTable($name, $options = array()) { if ($name instanceof Horde_Db_Adapter_Base_TableDefinition) { $options = array_merge($name->getOptions(), $options); } if (isset($options['options'])) { $opts = $options['options']; } else { if (empty($options['charset'])) { $options['charset'] = $this->getCharset(); } $opts = 'ENGINE=InnoDB DEFAULT CHARSET=' . $options['charset']; } return parent::endTable($name, array_merge(array('options' => $opts), $options)); } /** * Renames a table. * * @param string $name A table name. * @param string $newName The new table name. */ public function renameTable($name, $newName) { $this->_clearTableCache($name); $sql = sprintf('ALTER TABLE %s RENAME %s', $this->quoteTableName($name), $this->quoteTableName($newName)); return $this->execute($sql); } /** * Changes an existing column's definition. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $type A data type. * @param array $options Column options. See * Horde_Db_Adapter_Base_TableDefinition#column() * for details. */ public function changeColumn($tableName, $columnName, $type, $options = array()) { $this->_clearTableCache($tableName); $quotedTableName = $this->quoteTableName($tableName); $quotedColumnName = $this->quoteColumnName($columnName); $options = array_merge( array('limit' => null, 'precision' => null, 'scale' => null, 'unsigned' => null), $options); $sql = sprintf('SHOW COLUMNS FROM %s LIKE %s', $quotedTableName, $this->quoteString($columnName)); $row = $this->selectOne($sql); if (!array_key_exists('default', $options)) { $options['default'] = $row['Default']; $options['column'] = $this->makeColumn($columnName, $row['Default'], $row['Type'], $row['Null'] == 'YES'); } $typeSql = $this->typeToSql($type, $options['limit'], $options['precision'], $options['scale'], $options['unsigned']); $dropPk = ($type == 'autoincrementKey' && $row['Key'] == 'PRI') ? 'DROP PRIMARY KEY,' : ''; $sql = sprintf('ALTER TABLE %s %s CHANGE %s %s %s', $quotedTableName, $dropPk, $quotedColumnName, $quotedColumnName, $typeSql); if ($type != 'autoincrementKey') { $sql = $this->addColumnOptions($sql, $options); } $this->execute($sql); } /** * Sets a new default value for a column. * * If you want to set the default value to NULL, you are out of luck. You * need to execute the apppropriate SQL statement yourself. * * @param string $tableName A table name. * @param string $columnName A column name. * @param mixed $default The new default value. */ public function changeColumnDefault($tableName, $columnName, $default) { $this->_clearTableCache($tableName); $quotedTableName = $this->quoteTableName($tableName); $quotedColumnName = $this->quoteColumnName($columnName); $sql = sprintf('SHOW COLUMNS FROM %s LIKE %s', $quotedTableName, $this->quoteString($columnName)); $res = $this->selectOne($sql); $column = $this->makeColumn($columnName, $res['Default'], $res['Type'], $res['Null'] == 'YES'); $default = $this->quote($default, $column); $sql = sprintf('ALTER TABLE %s CHANGE %s %s %s DEFAULT %s', $quotedTableName, $quotedColumnName, $quotedColumnName, $res['Type'], $default); return $this->execute($sql); } /** * Renames a column. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $newColumnName The new column name. */ public function renameColumn($tableName, $columnName, $newColumnName) { $this->_clearTableCache($tableName); $quotedTableName = $this->quoteTableName($tableName); $quotedColumnName = $this->quoteColumnName($columnName); $sql = sprintf('SHOW COLUMNS FROM %s LIKE %s', $quotedTableName, $this->quoteString($columnName)); $res = $this->selectOne($sql); $currentType = $res['Type']; $sql = sprintf('ALTER TABLE %s CHANGE %s %s %s', $quotedTableName, $quotedColumnName, $this->quoteColumnName($newColumnName), $currentType); return $this->execute($sql); } /** * Removes a primary key from a table. * * @param string $tableName A table name. * * @throws Horde_Db_Exception */ public function removePrimaryKey($tableName) { $this->_clearTableCache($tableName); $sql = sprintf('ALTER TABLE %s DROP PRIMARY KEY', $this->quoteTableName($tableName)); return $this->execute($sql); } /** * Builds the name for an index. * * Cuts the index name to the maximum length of 64 characters limited by * MySQL. * * @param string $tableName A table name. * @param string|array $options Either a column name or index options: * - column: (string|array) column name(s). * - name: (string) the index name to fall * back to if no column names specified. */ public function indexName($tableName, $options=array()) { $indexName = parent::indexName($tableName, $options); if (strlen($indexName) > 64) { $indexName = substr($indexName, 0, 64); } return $indexName; } /** * Creates a database. * * @param string $name A database name. * @param array $options Database options. */ public function createDatabase($name, $options = array()) { return $this->execute("CREATE DATABASE `$name`"); } /** * Drops a database. * * @param string $name A database name. */ public function dropDatabase($name) { return $this->execute("DROP DATABASE IF EXISTS `$name`"); } /** * Returns the name of the currently selected database. * * @return string The database name. */ public function currentDatabase() { return $this->selectValue('SELECT DATABASE() AS db'); } /** * Generates the SQL definition for a column type. * * @param string $type A column type. * @param integer $limit Maximum column length (non decimal type only) * @param integer $precision The number precision (decimal type only). * @param integer $scale The number scaling (decimal columns only). * @param boolean $unsigned Whether the column is an unsigned number * (non decimal columns only). * * @return string The SQL definition. If $type is not one of the * internally supported types, $type is returned unchanged. */ public function typeToSql($type, $limit = null, $precision = null, $scale = null, $unsigned = null) { // If there is no explicit limit, adjust $nativeLimit for unsigned // integers. if ($type == 'integer' && !empty($unsigned) && empty($limit)) { $natives = $this->nativeDatabaseTypes(); $native = isset($natives[$type]) ? $natives[$type] : null; if (empty($native)) { return $type; } $nativeLimit = is_array($native) ? $native['limit'] : null; if (is_integer($nativeLimit)) { $limit = $nativeLimit - 1; } } $sql = parent::typeToSql($type, $limit, $precision, $scale, $unsigned); if (!empty($unsigned)) { $sql .= ' UNSIGNED'; } return $sql; } /** * Adds default/null options to column SQL definitions. * * @param string $sql Existing SQL definition for a column. * @param array $options Column options: * - null: (boolean) Whether to allow NULL values. * - default: (mixed) Default column value. * - autoincrement: (boolean) Whether the column is * an autoincrement column. Driver depedendent. * - after: (string) Insert column after this one. * MySQL specific. * * @return string The manipulated SQL definition. */ public function addColumnOptions($sql, $options) { $sql = parent::addColumnOptions($sql, $options); if (isset($options['after'])) { $sql .= ' AFTER ' . $this->quoteColumnName($options['after']); } if (!empty($options['autoincrement'])) { $sql .= ' AUTO_INCREMENT'; } return $sql; } /** * Returns an expression using the specified operator. * * @param string $lhs The column or expression to test. * @param string $op The operator. * @param string $rhs The comparison value. * @param boolean $bind If true, the method returns the query and a list * of values suitable for binding as an array. * @param array $params Any additional parameters for the operator. * * @return string|array The SQL test fragment, or an array containing the * query and a list of values if $bind is true. */ public function buildClause($lhs, $op, $rhs, $bind = false, $params = array()) { switch ($op) { case '~': if ($bind) { return array($lhs . ' REGEXP ?', array($rhs)); } else { return $lhs . ' REGEXP ' . $rhs; } } return parent::buildClause($lhs, $op, $rhs, $bind, $params); } /*########################################################################## # MySQL specific methods ##########################################################################*/ /** * Returns the character set of query results. * * @return string The result's charset. */ public function getCharset() { return $this->showVariable('character_set_results'); } /** * Sets the client and result charset. * * @param string $charset The character set to use for client queries and * results. */ public function setCharset($charset) { $charset = $this->_mysqlCharsetName($charset); $this->execute('SET NAMES ' . $this->quoteString($charset)); } /** * Returns the MySQL name of a character set. * * @param string $charset A charset name. * * @return string MySQL-normalized charset. */ public function _mysqlCharsetName($charset) { $charset = preg_replace(array('/[^a-z0-9]/', '/iso8859(\d)/'), array('', 'latin$1'), Horde_String::lower($charset)); $validCharsets = $this->selectValues('SHOW CHARACTER SET'); if (!in_array($charset, $validCharsets)) { throw new Horde_Db_Exception($charset . ' is not supported by MySQL (' . implode(', ', $validCharsets) . ')'); } return $charset; } /** * Returns the database collation strategy. * * @return string Database collation. */ public function getCollation() { return $this->showVariable('collation_database'); } /** * Returns a database variable. * * Convenience wrapper around "SHOW VARIABLES LIKE 'name'". * * @param string $name A variable name. * * @return string The variable value. * @throws Horde_Db_Exception */ public function showVariable($name) { $value = $this->selectOne('SHOW VARIABLES LIKE ' . $this->quoteString($name)); if ($value['Variable_name'] == $name) { return $value['Value']; } else { throw new Horde_Db_Exception($name . ' is not a recognized variable'); } } /** */ public function caseSensitiveEqualityOperator() { return '= BINARY'; } /** */ public function limitedUpdateConditions($whereSql, $quotedTableName, $quotedPrimaryKey) { return $whereSql; } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Mysqli/Result.php0000664000175000017500000001235212205351473020255 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Mysqli_Result implements Iterator { /** * @var Horde_Db_Adapter */ protected $_adapter; /** * @var string */ protected $_sql; /** * @var mixed */ protected $_arg1; /** * @var string */ protected $_arg2; /** * Result resource * @var mysqli_result */ protected $_result; /** * Current row * @var array */ protected $_current; /** * Current offset * @var integer */ protected $_index; /** * Are we at the end of the result? * @var boolean */ protected $_eof; /** * Which kind of keys to use for results. */ protected $_fetchMode = MYSQLI_ASSOC; /** * Constructor * * @param Horde_Db_Adapter $adapter * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function __construct($adapter, $sql, $arg1 = null, $arg2 = null) { $this->_adapter = $adapter; $this->_sql = $sql; $this->_arg1 = $arg1; $this->_arg2 = $arg2; } /** * Destructor - release any resources. */ public function __destruct() { if ($this->_result) { unset($this->_result); } } /** * Implementation of the rewind() method for iterator. */ public function rewind() { if ($this->_result) { unset($this->_result); } $this->_current = null; $this->_index = null; $this->_eof = true; $this->_result = $this->_adapter->execute($this->_sql, $this->_arg1, $this->_arg2); $this->next(); } /** * Implementation of the current() method for iterator. * * @return mixed The current row, or null if no rows. */ public function current() { if (is_null($this->_result)) { $this->rewind(); } return $this->_current; } /** * Implementation of the key() method for iterator. * * @return mixed The current row number (starts at 0), or NULL if no rows */ public function key() { if (is_null($this->_result)) { $this->rewind(); } return $this->_index; } /** * Implementation of the next() method. * * @return array|null The next row in the resultset or null if there are no * more results. */ public function next() { if (is_null($this->_result)) { $this->rewind(); } if ($this->_result) { $row = $this->_result->fetch_array($this->_fetchMode); if (!$row) { $this->_eof = true; } else { $this->_eof = false; if (is_null($this->_index)) { $this->_index = 0; } else { ++$this->_index; } $this->_current = $row; } } return $this->_current; } /** * Returns the current row and advances the recordset one row. * * @param integer $fetchmode The default fetch mode for this result. One * of the Horde_Db::FETCH_* constants. */ public function fetch($fetchmode = Horde_Db::FETCH_ASSOC) { if (!$this->valid()) { return null; } $this->setFetchMode($fetchmode); $row = $this->current(); $this->next(); return $row; } /** * Implementation of the valid() method for iterator * * @return boolean Whether the iteration is valid */ public function valid() { if (is_null($this->_result)) { $this->rewind(); } return !$this->_eof; } /** * Sets the default fetch mode for this result. * * @param integer $fetchmode One of the Horde_Db::FETCH_* constants. */ public function setFetchMode($fetchmode) { $map = array(Horde_Db::FETCH_ASSOC => MYSQL_ASSOC, Horde_Db::FETCH_NUM => MYSQL_NUM, Horde_Db::FETCH_BOTH => MYSQL_BOTH); $this->_fetchMode = $map[$fetchmode]; } /** * Returns the number of columns in the result set * * @return integer Number of columns. */ public function columnCount() { if (is_null($this->_result)) { $this->rewind(); } return $this->_result->field_count; } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Pdo/Base.php0000664000175000017500000001621512205351473017117 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ abstract class Horde_Db_Adapter_Pdo_Base extends Horde_Db_Adapter_Base { /*########################################################################## # Connection Management ##########################################################################*/ /** * Connect to the db */ public function connect() { if ($this->_active) { return; } list($dsn, $user, $pass) = $this->_parseConfig(); try { $pdo = @new PDO($dsn, $user, $pass); } catch (PDOException $e) { $msg = "Could not instantiate PDO with DSN \"$dsn\". PDOException: " . $e->getMessage(); throw new Horde_Db_Exception($msg); } $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); $this->_connection = $pdo; $this->_active = true; } /** * Check if the connection is active * * @return boolean */ public function isActive() { $this->_lastQuery = $sql = 'SELECT 1'; return isset($this->_connection) && $this->_connection->query($sql); } /*########################################################################## # Database Statements ##########################################################################*/ /** * Returns an array of record hashes with the column names as keys and * column values as values. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function selectAll($sql, $arg1=null, $arg2=null) { $result = $this->execute($sql, $arg1, $arg2); return $result ? $result->fetchAll(PDO::FETCH_ASSOC) : array(); } /** * Returns a record hash with the column names as keys and column values as * values. * * @param string $sql A query. * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * * @return array|boolean A record hash or false if no record found. */ public function selectOne($sql, $arg1 = null, $arg2 = null) { $result = $this->execute($sql, $arg1, $arg2); return $result ? $result->fetch(PDO::FETCH_ASSOC) : array(); } /** * Returns a single value from a record * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * @return string */ public function selectValue($sql, $arg1=null, $arg2=null) { $result = $this->execute($sql, $arg1, $arg2); return $result ? $result->fetchColumn(0) : null; } /** * Returns an array of the values of the first column in a select: * selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3] * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function selectValues($sql, $arg1=null, $arg2=null) { $result = $this->execute($sql, $arg1, $arg2); return $result ? $result->fetchAll(PDO::FETCH_COLUMN, 0) : array(); } /** * Returns an array where the keys are the first column of a select, and the * values are the second column: * * selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler'] * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function selectAssoc($sql, $arg1=null, $arg2=null) { $result = $this->execute($sql, $arg1, $arg2); return $result ? $result->fetchAll(PDO::FETCH_KEY_PAIR) : array(); } /*########################################################################## # Quoting ##########################################################################*/ /** * Quotes a string, escaping any ' (single quote) and \ (backslash) * characters.. * * @param string $string * @return string */ public function quoteString($string) { return $this->_connection->quote($string); } /*########################################################################## # Protected ##########################################################################*/ protected function _normalizeConfig($params) { // Normalize config parameters to what PDO expects. $normalize = array('database' => 'dbname', 'hostspec' => 'host'); foreach ($normalize as $from => $to) { if (isset($params[$from])) { $params[$to] = $params[$from]; unset($params[$from]); } } return $params; } protected function _buildDsnString($params) { $dsn = $this->_config['adapter'] . ':'; foreach ($params as $k => $v) { if (strlen($v)) { $dsn .= "$k=$v;"; } } return rtrim($dsn, ';'); } /** * Parse configuration array into options for PDO constructor. * * @throws Horde_Db_Exception * @return array [dsn, username, password] */ protected function _parseConfig() { $this->_checkRequiredConfig(array('adapter', 'username')); // try an empty password if it's not set. if (!isset($this->_config['password'])) { $this->_config['password'] = ''; } // collect options to build PDO Data Source Name (DSN) string $dsnOpts = $this->_config; unset( $dsnOpts['adapter'], $dsnOpts['username'], $dsnOpts['password'], $dsnOpts['protocol'], $dsnOpts['persistent'], $dsnOpts['charset'], $dsnOpts['phptype'], $dsnOpts['socket'] ); // return DSN and user/pass for connection return array( $this->_buildDsnString($this->_normalizeConfig($dsnOpts)), $this->_config['username'], $this->_config['password']); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Pdo/Mysql.php0000664000175000017500000000743512205351473017356 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * PDO_MySQL Horde_Db_Adapter * * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Pdo_Mysql extends Horde_Db_Adapter_Pdo_Base { /** * @var string */ protected $_schemaClass = 'Horde_Db_Adapter_Mysql_Schema'; /** * @return string */ public function adapterName() { return 'PDO_MySQL'; } /** * @return boolean */ public function supportsMigrations() { return true; } /*########################################################################## # Connection Management ##########################################################################*/ /** * Connect to the db */ public function connect() { if ($this->_active) { return; } parent::connect(); // ? $this->_connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true); // Set the default charset. http://dev.mysql.com/doc/refman/5.1/en/charset-connection.html if (!empty($this->_config['charset'])) { $this->setCharset($this->_config['charset']); } } /*########################################################################## # Protected ##########################################################################*/ /** * Parse configuration array into options for PDO constructor. * * http://pecl.php.net/bugs/7234 * Setting a bogus socket does not appear to work. * * @throws Horde_Db_Exception * @return array [dsn, username, password] */ protected function _parseConfig() { $this->_config['adapter'] = 'mysql'; $this->_checkRequiredConfig(array('adapter', 'username')); if (!empty($this->_config['socket'])) { $this->_config['unix_socket'] = $this->_config['socket']; unset($this->_config['socket']); } if (!empty($this->_config['host']) && $this->_config['host'] == 'localhost') { $this->_config['host'] = '127.0.0.1'; } // Try an empty password if it's not set. if (!isset($this->_config['password'])) { $this->_config['password'] = ''; } // Collect options to build PDO Data Source Name (DSN) string. $dsnOpts = $this->_config; unset($dsnOpts['adapter'], $dsnOpts['username'], $dsnOpts['password'], $dsnOpts['charset'], $dsnOpts['phptype']); $dsnOpts = $this->_normalizeConfig($dsnOpts); if (isset($dsnOpts['port'])) { if (empty($dsnOpts['host'])) { throw new Horde_Db_Exception('Host is required if port is specified'); } } if (isset($dsnOpts['unix_socket'])) { if (!empty($dsnOpts['host']) || !empty($dsnOpts['port'])) { throw new Horde_Db_Exception('Host and port must not be set if using a UNIX socket'); } } // Return DSN and user/pass for connection. return array( $this->_buildDsnString($dsnOpts), $this->_config['username'], $this->_config['password']); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Pdo/Pgsql.php0000664000175000017500000002250512205351473017332 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * PDO_PostgreSQL Horde_Db_Adapter * * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Pdo_Pgsql extends Horde_Db_Adapter_Pdo_Base { /** * @var string */ protected $_schemaClass = 'Horde_Db_Adapter_Postgresql_Schema'; /** * @return string */ public function adapterName() { return 'PDO_PostgreSQL'; } /** * @return boolean */ public function supportsMigrations() { return true; } /** * Does PostgreSQL support standard conforming strings? * @return boolean */ public function supportsStandardConformingStrings() { // Temporarily set the client message level above error to prevent unintentional // error messages in the logs when working on a PostgreSQL database server that // does not support standard conforming strings. $clientMinMessagesOld = $this->getClientMinMessages(); $this->setClientMinMessages('panic'); $hasSupport = $this->selectValue('SHOW standard_conforming_strings'); $this->setClientMinMessages($clientMinMessageOld); return $hasSupport; } public function supportsInsertWithReturning() { return $this->postgresqlVersion() >= 80200; } /*########################################################################## # Connection Management ##########################################################################*/ /** * Connect to the db. * * @throws Horde_Db_Exception */ public function connect() { if ($this->_active) { return; } parent::connect(); $this->_lastQuery = $sql = "SET datestyle TO 'iso'"; $retval = $this->_connection->exec($sql); if ($retval === false) { $error = $this->_connection->errorInfo(); throw new Horde_Db_Exception($error[2]); } // Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of // PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision // should know about this but can't detect it there, so deal with it here. Horde_Db_Adapter_Postgresql_Column::$moneyPrecision = ($this->postgresqlVersion() >= 80300) ? 19 : 10; $this->_configureConnection(); } /*########################################################################## # Database Statements ##########################################################################*/ /** * Inserts a row into a table. * * @param string $sql SQL statement. * @param array|string $arg1 Either an array of bound parameters or a * query name. * @param string $arg2 If $arg1 contains bound parameters, the * query name. * @param string $pk The primary key column. * @param integer $idValue The primary key value. This parameter is * required if the primary key is inserted * manually. * @param string $sequenceName The sequence name. * * @return integer Last inserted ID. * @throws Horde_Db_Exception */ public function insert($sql, $arg1 = null, $arg2 = null, $pk = null, $idValue = null, $sequenceName = null) { // Extract the table from the insert sql. Yuck. $temp = explode(' ', trim($sql), 4); $table = str_replace('"', '', $temp[2]); // Try an insert with 'returning id' if available (PG >= 8.2) if ($this->supportsInsertWithReturning()) { if (!$pk) { list($pk, $sequenceName) = $this->pkAndSequenceFor($table); } if ($pk) { $id = $this->selectValue($sql . ' RETURNING ' . $this->quoteColumnName($pk), $arg1, $arg2); $this->resetPkSequence($table, $pk, $sequenceName); return $id; } } // If neither pk nor sequence name is given, look them up. if (!($pk || $sequenceName)) { list($pk, $sequenceName) = $this->pkAndSequenceFor($table); } // Otherwise, insert then grab last_insert_id. if ($insertId = parent::insert($sql, $arg1, $arg2, $pk, $idValue, $sequenceName)) { $this->resetPkSequence($table, $pk, $sequenceName); return $insertId; } // If a pk is given, fallback to default sequence name. // Don't fetch last insert id for a table without a pk. if ($pk && ($sequenceName || $sequenceName = $this->defaultSequenceName($table, $pk))) { $this->resetPkSequence($table, $pk, $sequenceName); return $this->_lastInsertId($table, $sequenceName); } } /** * Appends LIMIT and OFFSET options to a SQL statement. * * @param string $sql SQL statement. * @param array $options Hash with 'limit' and (optional) 'offset' values. * * @return string */ public function addLimitOffset($sql, $options) { if (isset($options['limit']) && $limit = $options['limit']) { $sql .= " LIMIT $limit"; } if (isset($options['offset']) && $offset = $options['offset']) { $sql .= " OFFSET $offset"; } return $sql; } /*########################################################################## # Protected ##########################################################################*/ /** * Parse configuration array into options for PDO constructor. * * @throws Horde_Db_Exception * @return array [dsn, username, password] */ protected function _parseConfig() { $this->_config['adapter'] = 'pgsql'; // PDO for PostgreSQL does not accept a socket argument // in the connection string; the location can be set via the // "host" argument instead. if (!empty($this->_config['socket'])) { $this->_config['host'] = $this->_config['socket']; unset($this->_config['socket']); } return parent::_parseConfig(); } /** * Configures the encoding, verbosity, and schema search path of the connection. * This is called by connect() and should not be called manually. */ protected function _configureConnection() { if (!empty($this->_config['charset'])) { $this->_lastQuery = $sql = 'SET client_encoding TO '.$this->quoteString($this->_config['charset']); $this->execute($sql); } if (!empty($this->_config['client_min_messages'])) $this->setClientMinMessages($this->_config['client_min_messages']); $this->setSchemaSearchPath(!empty($this->_config['schema_search_path']) || !empty($this->_config['schema_order'])); } /** * @TODO */ protected function _selectRaw($sql, $arg1=null, $arg2=null) { $result = $this->execute($sql, $arg1, $arg2); if (!$result) return array(); $moneyFields = array(); for ($i = 0, $i_max = $result->columnCount(); $i < $i_max; $i++) { $f = $result->getColumnMeta($i); if (!empty($f['pgsql:oid']) && $f['pgsql:oid'] == Horde_Db_Adapter_Postgresql_Column::MONEY_COLUMN_TYPE_OID) { $moneyFields[] = $i; $moneyFields[] = $f['name']; } } foreach ($result as $row) { // If this is a money type column and there are any currency // symbols, then strip them off. Indeed it would be prettier to do // this in Horde_Db_Adapter_Postgres_Column::stringToDecimal but // would break form input fields that call valueBeforeTypeCast. foreach ($moneyFields as $f) { // Because money output is formatted according to the locale, there are two // cases to consider (note the decimal separators): // (1) $12,345,678.12 // (2) $12.345.678,12 if (preg_match('/^-?\D+[\d,]+\.\d{2}$/', $row[$f])) { // #1 $row[$f] = preg_replace('/[^-\d\.]/', '', $row[$f]) . "\n"; } elseif (preg_match('/^-?\D+[\d\.]+,\d{2}$/', $row[$f])) { // #2 $row[$f] = str_replace(',', '.', preg_replace('/[^-\d,]/', '', $row[$f])) . "\n"; } } $rows[] = $row; } $result->closeCursor(); return $rows; } /** * Returns the current ID of a table's sequence. */ protected function _lastInsertId($table, $sequenceName) { return (int)$this->selectValue('SELECT currval('.$this->quoteSequenceName($sequenceName).')'); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Pdo/Sqlite.php0000664000175000017500000001343312205351473017505 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * PDO_SQLite Horde_Db_Adapter * * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Pdo_Sqlite extends Horde_Db_Adapter_Pdo_Base { /** * @var string */ protected $_schemaClass = 'Horde_Db_Adapter_Sqlite_Schema'; /** * SQLite version number * @var integer */ protected $_sqliteVersion; /** * @return string */ public function adapterName() { return 'PDO_SQLite'; } /** * @return boolean */ public function supportsMigrations() { return true; } /** * Does this adapter support using DISTINCT within COUNT? This is +true+ * for all adapters except sqlite. * * @return boolean */ public function supportsCountDistinct() { return $this->_sqliteVersion >= '3.2.6'; } public function supportsAutoIncrement() { return $this->_sqliteVersion >= '3.1.0'; } /*########################################################################## # Connection Management ##########################################################################*/ /** * Connect to the db. * * @throws Horde_Db_Exception */ public function connect() { if ($this->_active) { return; } parent::connect(); $this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true); $this->_lastQuery = $sql = 'PRAGMA full_column_names=0'; $retval = $this->_connection->exec($sql); if ($retval === false) { $error = $this->_connection->errorInfo(); throw new Horde_Db_Exception($error[2]); } $this->_lastQuery = $sql = 'PRAGMA short_column_names=1'; $retval = $this->_connection->exec($sql); if ($retval === false) { $error = $this->_connection->errorInfo(); throw new Horde_Db_Exception($error[2]); } $this->_lastQuery = $sql = 'SELECT sqlite_version(*)'; $this->_sqliteVersion = $this->selectValue($sql); } /*########################################################################## # Database Statements ##########################################################################*/ /** * Executes the SQL statement in the context of this connection. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function execute($sql, $arg1=null, $arg2=null) { return $this->_catchSchemaChanges('execute', array($sql, $arg1, $arg2)); } /** * Begins the transaction (and turns off auto-committing). */ public function beginDbTransaction() { return $this->_catchSchemaChanges('beginDbTransaction'); } /** * Commits the transaction (and turns on auto-committing). */ public function commitDbTransaction() { return $this->_catchSchemaChanges('commitDbTransaction'); } /** * Rolls back the transaction (and turns on auto-committing). Must be * done if the transaction block raises an exception or returns false. */ public function rollbackDbTransaction() { return $this->_catchSchemaChanges('rollbackDbTransaction'); } /** * SELECT ... FOR UPDATE is redundant since the table is locked. */ public function addLock(&$sql, array $options = array()) { } public function emptyInsertStatement($tableName) { return 'INSERT INTO '.$this->quoteTableName($tableName).' VALUES(NULL)'; } /*########################################################################## # Protected ##########################################################################*/ protected function _catchSchemaChanges($method, $args = array()) { try { return call_user_func_array(array($this, "parent::$method"), $args); } catch (Exception $e) { if (preg_match('/database schema has changed/i', $e->getMessage())) { $this->reconnect(); return call_user_func_array(array($this, "parent::$method"), $args); } else { throw $e; } } } protected function _buildDsnString($params) { return 'sqlite:' . $params['dbname']; } /** * Parse configuration array into options for PDO constructor * * @throws Horde_Db_Exception * @return array [dsn, username, password] */ protected function _parseConfig() { // check required config keys are present if (empty($this->_config['database']) && empty($this->_config['dbname'])) { $msg = 'Either dbname or database is required'; throw new Horde_Db_Exception($msg); } // collect options to build PDO Data Source Name (DSN) string $dsnOpts = $this->_config; unset($dsnOpts['adapter'], $dsnOpts['username'], $dsnOpts['password']); // return DSN and dummy user/pass for connection return array($this->_buildDsnString($this->_normalizeConfig($dsnOpts)), '', ''); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Postgresql/Column.php0000664000175000017500000001711512205351473021123 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Postgresql_Column extends Horde_Db_Adapter_Base_Column { /*########################################################################## # Constants ##########################################################################*/ /** * The internal PostgreSQL identifier of the money data type. * @const integer */ const MONEY_COLUMN_TYPE_OID = 790; /** * @var integer */ public static $moneyPrecision = 19; /** * @var array */ protected static $_hasEmptyStringDefault = array('binary', 'string', 'text'); /** * Construct * @param string $name * @param string $default * @param string $sqlType * @param boolean $null */ public function __construct($name, $default, $sqlType=null, $null=true) { parent::__construct($name, $this->_extractValueFromDefault($default), $sqlType, $null); } /** * @param string $fieldType * @return string */ protected function _simplifiedType($fieldType) { switch (true) { // Numeric and monetary types case preg_match('/^(?:real|double precision)$/', $fieldType): return 'float'; // Monetary types case preg_match('/^money$/', $fieldType): return 'decimal'; // Character types case preg_match('/^(?:character varying|bpchar)(?:\(\d+\))?$/', $fieldType): return 'string'; // Binary data types case preg_match('/^bytea$/', $fieldType): return 'binary'; // Date/time types case preg_match('/^timestamp with(?:out)? time zone$/', $fieldType): return 'datetime'; case preg_match('/^interval$/', $fieldType): return 'string'; // Geometric types case preg_match('/^(?:point|line|lseg|box|"?path"?|polygon|circle)$/', $fieldType): return 'string'; // Network address types case preg_match('/^(?:cidr|inet|macaddr)$/', $fieldType): return 'string'; // Bit strings case preg_match('/^bit(?: varying)?(?:\(\d+\))?$/', $fieldType): return 'string'; // XML type case preg_match('/^xml$/', $fieldType): return 'string'; // Arrays case preg_match('/^\D+\[\]$/', $fieldType): return 'string'; // Object identifier types case preg_match('/^oid$/', $fieldType): return 'integer'; } // Pass through all types that are not specific to PostgreSQL. return parent::_simplifiedType($fieldType); } /** * Extracts the value from a PostgreSQL column default definition. */ protected function _extractValueFromDefault($default) { switch (true) { // Numeric types case preg_match('/\A-?\d+(\.\d*)?\z/', $default): return $default; // Character types case preg_match('/\A\'(.*)\'::(?:character varying|bpchar|text)\z/m', $default, $matches): return $matches[1]; // Character types (8.1 formatting) case preg_match('/\AE\'(.*)\'::(?:character varying|bpchar|text)\z/m', $default, $matches): /*@TODO fix preg callback*/ return preg_replace('/\\(\d\d\d)/', '$1.oct.chr', $matches[1]); // Binary data types case preg_match('/\A\'(.*)\'::bytea\z/m', $default, $matches): return $matches[1]; // Date/time types case preg_match('/\A\'(.+)\'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/', $default, $matches): return $matches[1]; case preg_match('/\A\'(.*)\'::interval\z/', $default, $matches): return $matches[1]; // Boolean type case $default == 'true': return true; case $default == 'false': return false; // Geometric types case preg_match('/\A\'(.*)\'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/', $default, $matches): return $matches[1]; // Network address types case preg_match('/\A\'(.*)\'::(?:cidr|inet|macaddr)\z/', $default, $matches): return $matches[1]; // Bit string types case preg_match('/\AB\'(.*)\'::"?bit(?: varying)?"?\z/', $default, $matches): return $matches[1]; // XML type case preg_match('/\A\'(.*)\'::xml\z/m', $default, $matches): return $matches[1]; // Arrays case preg_match('/\A\'(.*)\'::"?\D+"?\[\]\z/', $default, $matches): return $matches[1]; // Object identifier types case preg_match('/\A-?\d+\z/', $default, $matches): return $matches[1]; default: // Anything else is blank, some user type, or some function // and we can't know the value of that, so return nil. return null; } } /** * Used to convert from BLOBs (BYTEAs) to Strings. * * @return string */ public function binaryToString($value) { if (is_resource($value)) { $string = stream_get_contents($value); fclose($value); return $string; } return preg_replace_callback("/(?:\\\'|\\\\\\\\|\\\\\d{3})/", array($this, 'binaryToStringCallback'), $value); } /** * Callback function for binaryToString(). */ public function binaryToStringCallback($matches) { if ($matches[0] == '\\\'') { return "'"; } elseif ($matches[0] == '\\\\\\\\') { return '\\'; } return chr(octdec(substr($matches[0], -3))); } /*########################################################################## # Protected ##########################################################################*/ /** * @param string $sqlType * @return int */ protected function _extractLimit($sqlType) { if (preg_match('/^bigint/i', $sqlType)) { return 8; } if (preg_match('/^smallint/i', $sqlType)) { return 2; } return parent::_extractLimit($sqlType); } /** * @param string $sqlType * @return int */ protected function _extractPrecision($sqlType) { if (preg_match('/^money/', $sqlType)) { return self::$moneyPrecision; } return parent::_extractPrecision($sqlType); } /** * @param string $sqlType * @return int */ protected function _extractScale($sqlType) { if (preg_match('/^money/', $sqlType)) { return 2; } return parent::_extractScale($sqlType); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Postgresql/Schema.php0000664000175000017500000012561012205351473021066 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Postgresql_Schema extends Horde_Db_Adapter_Base_Schema { /** * The active schema search path. * * @var string */ protected $_schemaSearchPath = ''; /** * Cached version. * * @var integer */ protected $_version; /*########################################################################## # Object factories ##########################################################################*/ /** * Factory for Column objects. * * @param string $name The column's name, such as "supplier_id" in * "supplier_id int(11)". * @param string $default The type-casted default value, such as "new" in * "sales_stage varchar(20) default 'new'". * @param string $sqlType Used to extract the column's type, length and * signed status, if necessary. For example * "varchar" and "60" in "company_name varchar(60)" * or "unsigned => true" in "int(10) UNSIGNED". * @param boolean $null Whether this column allows NULL values. * * @return Horde_Db_Adapter_Postgresql_Column A column object. */ public function makeColumn($name, $default, $sqlType = null, $null = true) { return new Horde_Db_Adapter_Postgresql_Column($name, $default, $sqlType, $null); } /*########################################################################## # Quoting ##########################################################################*/ /** * Quotes the column value to help prevent SQL injection attacks. * * This method makes educated guesses on the scalar type based on the * passed value. Make sure to correctly cast the value and/or pass the * $column parameter to get the best results. * * @param mixed $value The scalar value to quote, a Horde_Db_Value, * Horde_Date, or DateTime instance, or an object * implementing quotedId(). * @param object $column An object implementing getType(). * * @return string The correctly quoted value. */ public function quote($value, $column = null) { if (!$column) { return parent::quote($value, $column); } if (is_string($value) && $column->getType() == 'binary') { return $this->quoteBinary($value); } if (is_string($value) && $column->getSqlType() == 'xml') { return "xml '" . $this->quoteString($value) . "'"; } if (is_numeric($value) && $column->getSqlType() == 'money') { // Not truly string input, so doesn't require (or allow) escape // string syntax. return "'" . $value . "'"; } if (is_string($value) && substr($column->getSqlType(), 0, 3) == 'bit') { if (preg_match('/^[01]*$/', $value)) { // Bit-string notation return "B'" . $value . "'"; } if (preg_match('/^[0-9A-F]*$/i')) { // Hexadecimal notation return "X'" . $value . "'"; } } return parent::quote($value, $column); } /** * Returns a quoted form of the column name. * * @param string $name A column name. * * @return string The quoted column name. */ public function quoteColumnName($name) { return '"' . str_replace('"', '""', $name) . '"'; } /** * Returns a quoted sequence name. * * PostgreSQL specific method. * * @param string $name A sequence name. * * @return string The quoted sequence name. */ public function quoteSequenceName($name) { return '\'' . str_replace('"', '""', $name) . '\''; } /** * Returns a quoted binary value. * * @param mixed A binary value. * * @return string The quoted binary value. */ public function quoteBinary($value) { if ($this->postgresqlVersion() >= 90000) { return "E'\\\\x" . bin2hex($value) . "'"; } /* MUST escape zero octet(0), single quote (39), and backslash (92). * MAY escape non-printable octets, but they are required in some * instances so it is best to escape all. */ return "E'" . preg_replace_callback("/[\\x00-\\x1f\\x27\\x5c\\x7f-\\xff]/", array($this, '_quoteBinaryCallback'), $value) . "'"; } /** * Callback function for quoteBinary(). * * @param array $matches Matches from preg_replace(). * * @return string Escaped/encoded binary value. */ protected function _quoteBinaryCallback($matches) { return sprintf('\\\\%03.o', ord($matches[0])); } /*########################################################################## # Schema Statements ##########################################################################*/ /** * Returns a hash of mappings from the abstract data types to the native * database types. * * See TableDefinition::column() for details on the recognized abstract * data types. * * @see TableDefinition::column() * * @return array A database type map. */ public function nativeDatabaseTypes() { return array( 'autoincrementKey' => 'serial primary key', 'string' => array('name' => 'character varying', 'limit' => 255), 'text' => array('name' => 'text', 'limit' => null), 'mediumtext' => array('name' => 'text', 'limit' => null), 'longtext' => array('name' => 'text', 'limit' => null), 'integer' => array('name' => 'integer', 'limit' => null), 'float' => array('name' => 'float', 'limit' => null), 'decimal' => array('name' => 'decimal', 'limit' => null), 'datetime' => array('name' => 'timestamp', 'limit' => null), 'timestamp' => array('name' => 'timestamp', 'limit' => null), 'time' => array('name' => 'time', 'limit' => null), 'date' => array('name' => 'date', 'limit' => null), 'binary' => array('name' => 'bytea', 'limit' => null), 'boolean' => array('name' => 'boolean', 'limit' => null), ); } /** * Returns the maximum length a table alias can have. * * Returns the configured supported identifier length supported by * PostgreSQL, or report the default of 63 on PostgreSQL 7.x. * * @return integer The maximum table alias length. */ public function tableAliasLength() { if ($this->postgresqlVersion() >= 80000) { return (int)$this->selectValue('SHOW max_identifier_length'); } return 63; } /** * Returns a list of all tables in the schema search path. * * @return array A table list. */ public function tables() { $schemas = array(); foreach (explode(',', $this->getSchemaSearchPath()) as $p) { $schemas[] = $this->quote($p); } return $this->selectValues('SELECT tablename FROM pg_tables WHERE schemaname IN (' . implode(',', $schemas) . ')'); } /** * Returns a table's primary key. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return Horde_Db_Adapter_Base_Index The primary key index object. */ public function primaryKey($tableName, $name = null) { $sql = ' SELECT column_name FROM information_schema.constraint_column_usage WHERE table_name = ? AND constraint_name = (SELECT constraint_name FROM information_schema.table_constraints WHERE table_name = ? AND constraint_type = ?)'; $pk = $this->selectValues($sql, array($tableName, $tableName, 'PRIMARY KEY'), $name); return $this->makeIndex($tableName, 'PRIMARY', true, true, $pk); } /** * Returns a list of tables indexes. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return array A list of Horde_Db_Adapter_Base_Index objects. */ public function indexes($tableName, $name = null) { $indexes = @unserialize($this->_cache->get("tables/indexes/$tableName", 0)); if (!$indexes) { $schemas = array(); foreach (explode(',', $this->getSchemaSearchPath()) as $p) { $schemas[] = $this->quote($p); } $sql = " SELECT distinct i.relname, d.indisunique, a.attname FROM pg_class t, pg_class i, pg_index d, pg_attribute a WHERE i.relkind = 'i' AND d.indexrelid = i.oid AND d.indisprimary = 'f' AND t.oid = d.indrelid AND t.relname = " . $this->quote($tableName) . " AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (" . implode(',', $schemas) . ") ) AND a.attrelid = t.oid AND (d.indkey[0] = a.attnum OR d.indkey[1] = a.attnum OR d.indkey[2] = a.attnum OR d.indkey[3] = a.attnum OR d.indkey[4] = a.attnum OR d.indkey[5] = a.attnum OR d.indkey[6] = a.attnum OR d.indkey[7] = a.attnum OR d.indkey[8] = a.attnum OR d.indkey[9] = a.attnum) ORDER BY i.relname"; $result = $this->select($sql, $name); $currentIndex = null; $indexes = array(); foreach ($result as $row) { if ($currentIndex != $row['relname']) { $currentIndex = $row['relname']; $indexes[] = $this->makeIndex( $tableName, $row['relname'], false, $row['indisunique'] == 't', array()); } $indexes[count($indexes) - 1]->columns[] = $row['attname']; } $this->_cache->set("tables/indexes/$tableName", serialize($indexes)); } return $indexes; } /** * Returns a list of table columns. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return array A list of Horde_Db_Adapter_Base_Column objects. */ public function columns($tableName, $name = null) { $rows = @unserialize($this->_cache->get("tables/columns/$tableName", 0)); if (!$rows) { $rows = $this->_columnDefinitions($tableName, $name); $this->_cache->set("tables/columns/$tableName", serialize($rows)); } // Create columns from rows. $columns = array(); foreach ($rows as $row) { $columns[$row['attname']] = $this->makeColumn( $row['attname'], $row['adsrc'], $row['format_type'], !(boolean)$row['attnotnull']); } return $columns; } /** * Returns the list of a table's column names, data types, and default * values. * * The underlying query is roughly: * SELECT column.name, column.type, default.value * FROM column LEFT JOIN default * ON column.table_id = default.table_id * AND column.num = default.column_num * WHERE column.table_id = get_table_id('table_name') * AND column.num > 0 * AND NOT column.is_dropped * ORDER BY column.num * * If the table name is not prefixed with a schema, the database will take * the first match from the schema search path. * * Query implementation notes: * - format_type includes the column size constraint, e.g. varchar(50) * - ::regclass is a function that gives the id for a table name */ protected function _columnDefinitions($tableName, $name = null) { /* @todo See if we can get this from information_schema instead */ return $this->selectAll(' SELECT a.attname, format_type(a.atttypid, a.atttypmod), d.adsrc, a.attnotnull FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE a.attrelid = ' . $this->quote($tableName) . '::regclass AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum', $name); } /** * Renames a table. * * @param string $name A table name. * @param string $newName The new table name. */ public function renameTable($name, $newName) { $this->_clearTableCache($name); return $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($name), $this->quoteTableName($newName))); } /** * Adds a new column to a table. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $type A data type. * @param array $options Column options. See * Horde_Db_Adapter_Base_TableDefinition#column() * for details. */ public function addColumn($tableName, $columnName, $type, $options = array()) { $this->_clearTableCache($tableName); $options = array_merge( array('autoincrement' => null, 'limit' => null, 'precision' => null, 'scale' => null), $options); $sqltype = $this->typeToSql($type, $options['limit'], $options['precision'], $options['scale']); /* Convert to SERIAL type if needed. */ if ($options['autoincrement']) { switch ($sqltype) { case 'bigint': $sqltype = 'BIGSERIAL'; break; case 'integer': default: $sqltype = 'SERIAL'; break; } } // Add the column. $sql = sprintf('ALTER TABLE %s ADD COLUMN %s %s', $this->quoteTableName($tableName), $this->quoteColumnName($columnName), $sqltype); $this->execute($sql); if (array_key_exists('default', $options)) { $sql = sprintf('UPDATE %s SET %s = %s', $this->quoteTableName($tableName), $this->quoteColumnName($columnName), $this->quote($options['default'])); $this->execute($sql); $this->changeColumnDefault($tableName, $columnName, $options['default']); } if (isset($options['null']) && $options['null'] === false) { $this->changeColumnNull( $tableName, $columnName, false, isset($options['default']) ? $options['default'] : null); } } /** * Changes an existing column's definition. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $type A data type. * @param array $options Column options. See * Horde_Db_Adapter_Base_TableDefinition#column() * for details. */ public function changeColumn($tableName, $columnName, $type, $options = array()) { $this->_clearTableCache($tableName); $options = array_merge( array('autoincrement' => null, 'limit' => null, 'precision' => null, 'scale' => null), $options); $quotedTableName = $this->quoteTableName($tableName); $primaryKey = $type == 'autoincrementKey'; if ($primaryKey) { $type = 'integer'; $options['autoincrement'] = true; $options['limit'] = $options['precision'] = $options['scale'] = null; try { $this->removePrimaryKey($tableName); } catch (Horde_Db_Exception $e) { } } $sql = sprintf('ALTER TABLE %s ALTER COLUMN %s TYPE %s', $quotedTableName, $this->quoteColumnName($columnName), $this->typeToSql($type, $options['limit'], $options['precision'], $options['scale'])); try { $this->execute($sql); } catch (Horde_Db_Exception $e) { // This is PostgreSQL 7.x, or the old type could not be coerced to // the new type, so we have to use a more arcane way of doing it. try { // Booleans can't always be cast to other data types; do extra // work to handle them. $oldType = null; foreach ($this->columns($tableName) as $column) { if ($column->getName() == $columnName) { $oldType = $column->getType(); break; } } if ($oldType === null) { throw new Horde_Db_Exception("$tableName does not have a column '$columnName'"); } $this->beginDbTransaction(); $tmpColumnName = $columnName.'_change_tmp'; $this->addColumn($tableName, $tmpColumnName, $type, $options); if ($oldType == 'boolean') { $sql = sprintf('UPDATE %s SET %s = CAST(CASE WHEN %s IS TRUE THEN 1 ELSE 0 END AS %s)', $quotedTableName, $this->quoteColumnName($tmpColumnName), $this->quoteColumnName($columnName), $this->typeToSql($type, $options['limit'], $options['precision'], $options['scale'])); } else { $sql = sprintf('UPDATE %s SET %s = CAST(%s AS %s)', $quotedTableName, $this->quoteColumnName($tmpColumnName), $this->quoteColumnName($columnName), $this->typeToSql($type, $options['limit'], $options['precision'], $options['scale'])); } $this->execute($sql); $this->removeColumn($tableName, $columnName); $this->renameColumn($tableName, $tmpColumnName, $columnName); $this->commitDbTransaction(); } catch (Horde_Db_Exception $e) { $this->rollbackDbTransaction(); throw $e; } } if ($options['autoincrement']) { $seq_name = $this->defaultSequenceName($tableName, $columnName); try { $this->execute('DROP SEQUENCE ' . $seq_name . ' CASCADE'); } catch (Horde_Db_Exception $e) {} $this->execute('CREATE SEQUENCE ' . $seq_name); $this->resetPkSequence($tableName, $columnName, $seq_name); /* Can't use changeColumnDefault() since it quotes the * default value (NEXTVAL is a postgres keyword, not a text * value). */ $this->_clearTableCache($tableName); $sql = sprintf('ALTER TABLE %s ALTER COLUMN %s SET DEFAULT NEXTVAL(%s)', $this->quoteTableName($tableName), $this->quoteColumnName($columnName), $this->quoteSequenceName($seq_name)); $this->execute($sql); if ($this->postgresqlVersion() >= 80200) { $sql = sprintf('ALTER SEQUENCE %s OWNED BY %s.%s', $seq_name, $this->quoteTableName($tableName), $this->quoteColumnName($columnName)); $this->execute($sql); } } elseif (array_key_exists('default', $options)) { $this->changeColumnDefault($tableName, $columnName, $options['default']); } if ($primaryKey) { $this->addPrimaryKey($tableName, $columnName); } if (array_key_exists('null', $options)) { $this->changeColumnNull( $tableName, $columnName, $options['null'], isset($options['default']) ? $options['default'] : null); } } /** * Sets a new default value for a column. * * If you want to set the default value to NULL, you are out of luck. You * need to execute the apppropriate SQL statement yourself. * * @param string $tableName A table name. * @param string $columnName A column name. * @param mixed $default The new default value. */ public function changeColumnDefault($tableName, $columnName, $default) { $this->_clearTableCache($tableName); $sql = sprintf('ALTER TABLE %s ALTER COLUMN %s SET DEFAULT %s', $this->quoteTableName($tableName), $this->quoteColumnName($columnName), $this->quote($default)); return $this->execute($sql); } /** * Sets whether a column allows NULL values. * * @param string $tableName A table name. * @param string $columnName A column name. * @param boolean $null Whether NULL values are allowed. * @param mixed $default The new default value. */ public function changeColumnNull($tableName, $columnName, $null, $default = null) { $this->_clearTableCache($tableName); if (!$null && !is_null($default)) { $sql = sprintf('UPDATE %s SET %s = %s WHERE %s IS NULL', $this->quoteTableName($tableName), $this->quoteColumnName($columnName), $this->quote($default), $this->quoteColumnName($columnName)); $this->execute($sql); } $sql = sprintf('ALTER TABLE %s ALTER %s %s NOT NULL', $this->quoteTableName($tableName), $this->quoteColumnName($columnName), $null ? 'DROP' : 'SET'); return $this->execute($sql); } /** * Renames a column. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $newColumnName The new column name. */ public function renameColumn($tableName, $columnName, $newColumnName) { $this->_clearTableCache($tableName); $sql = sprintf('ALTER TABLE %s RENAME COLUMN %s TO %s', $this->quoteTableName($tableName), $this->quoteColumnName($columnName), $this->quoteColumnName($newColumnName)); return $this->execute($sql); } /** * Removes a primary key from a table. * * @param string $tableName A table name. * * @throws Horde_Db_Exception */ public function removePrimaryKey($tableName) { $this->_clearTableCache($tableName); $keyName = $this->selectValue( 'SELECT constraint_name FROM information_schema.table_constraints WHERE table_name = ? AND constraint_type = ?', array($tableName, 'PRIMARY KEY')); if ($keyName) { $sql = sprintf('ALTER TABLE %s DROP CONSTRAINT %s CASCADE', $this->quoteTableName($tableName), $this->quoteColumnName($keyName)); return $this->execute($sql); } } /** * Removes an index from a table. * * See parent class for examples. * * @param string $tableName A table name. * @param string|array $options Either a column name or index options: * - name: (string) the index name. * - column: (string|array) column name(s). */ public function removeIndex($tableName, $options = array()) { $this->_clearTableCache($tableName); return $this->execute('DROP INDEX ' . $this->indexName($tableName, $options)); } /** * Creates a database. * * @param string $name A database name. * @param array $options Database options: owner, template, charset, * tablespace, and connection_limit. */ public function createDatabase($name, $options = array()) { $options = array_merge(array('charset' => 'utf8'), $options); $optionString = ''; foreach ($options as $key => $value) { switch ($key) { case 'owner': $optionString .= " OWNER = '$value'"; break; case 'template': $optionString .= " TEMPLATE = $value"; break; case 'charset': $optionString .= " ENCODING = '$value'"; break; case 'tablespace': $optionString .= " TABLESPACE = $value"; break; case 'connection_limit': $optionString .= " CONNECTION LIMIT = $value"; } } return $this->execute('CREATE DATABASE ' . $this->quoteTableName($name) . $optionString); } /** * Drops a database. * * @param string $name A database name. */ public function dropDatabase($name) { if ($this->postgresqlVersion() >= 80200) { return $this->execute('DROP DATABASE IF EXISTS ' . $this->quoteTableName($name)); } try { return $this->execute('DROP DATABASE ' . $this->quoteTableName($name)); } catch (Horde_Db_Exception $e) { if ($this->_logger) { $this->_logger->warn("$name database doesn't exist"); } } } /** * Returns the name of the currently selected database. * * @return string The database name. */ public function currentDatabase() { return $this->selectValue('SELECT current_database()'); } /** * Generates the SQL definition for a column type. * * @param string $type A column type. * @param integer $limit Maximum column length (non decimal type only) * @param integer $precision The number precision (decimal type only). * @param integer $scale The number scaling (decimal columns only). * @param boolean $unsigned Whether the column is an unsigned number * (non decimal columns only). * * @return string The SQL definition. If $type is not one of the * internally supported types, $type is returned unchanged. */ public function typeToSql($type, $limit = null, $precision = null, $scale = null, $unsigned = null) { if ($type != 'integer') { return parent::typeToSql($type, $limit, $precision, $scale); } switch ($limit) { case 1: case 2: return 'smallint'; case 3: case 4: case null: return 'integer'; case 5: case 6: case 7: case 8: return 'bigint'; } throw new Horde_Db_Exception("No integer type has byte size $limit. Use a numeric with precision 0 instead."); } /** * Generates a DISTINCT clause for SELECT queries. * * PostgreSQL requires the ORDER BY columns in the SELECT list for distinct * queries, and requires that the ORDER BY include the DISTINCT column. * * * $connection->distinct('posts.id', 'posts.created_at DESC') * * * @param string $columns A column list. * @param string $orderBy An ORDER clause. * * @return string The generated DISTINCT clause. */ public function distinct($columns, $orderBy = null) { if (empty($orderBy)) { return 'DISTINCT ' . $columns; } // Construct a clean list of column names from the ORDER BY clause, // removing any ASC/DESC modifiers. $orderColumns = array(); foreach (preg_split('/\s*,\s*/', $orderBy, -1, PREG_SPLIT_NO_EMPTY) as $orderByClause) { $orderColumns[] = current(preg_split('/\s+/', $orderByClause, -1, PREG_SPLIT_NO_EMPTY)) . ' AS alias_' . count($orderColumns); } // Return a DISTINCT ON() clause that's distinct on the columns we want // but includes all the required columns for the ORDER BY to work // properly. return sprintf('DISTINCT ON (%s) %s, %s', $columns, $columns, implode(', ', $orderColumns)); } /** * Adds an ORDER BY clause to an existing query. * * PostgreSQL does not allow arbitrary ordering when using DISTINCT ON, so * we work around this by wrapping the $sql string as a sub-select and * ordering in that query. * * @param string $sql An SQL query to manipulate. * @param array $options Options: * - order: Order column an direction. * * @return string The manipulated SQL query. */ public function addOrderByForAssociationLimiting($sql, $options) { if (empty($options['order'])) { return $sql; } $order = array(); foreach (preg_split('/\s*,\s*/', $options['order'], -1, PREG_SPLIT_NO_EMPTY) as $s) { if (preg_match('/\bdesc$/i', $s)) { $s = 'DESC'; } $order[] = 'id_list.alias_' . count($order) . ' ' . $s; } $order = implode(', ', $order); return sprintf('SELECT * FROM (%s) AS id_list ORDER BY %s', $sql, $order); } /** * Generates an INTERVAL clause for SELECT queries. * * @param string $interval The interval. * @param string $precision The precision. * * @return string The generated INTERVAL clause. */ public function interval($interval, $precision) { return 'INTERVAL \'' . $interval . ' ' . $precision . '\''; } /** * Generates a modified date for SELECT queries. * * @param string $reference The reference date - this is a column * referenced in the SELECT. * @param string $operator Add or subtract time? (+/-) * @param integer $amount The shift amount (number of days if $interval * is DAY, etc). * @param string $interval The interval (SECOND, MINUTE, HOUR, DAY, * MONTH, YEAR). * * @return string The generated INTERVAL clause. */ public function modifyDate($reference, $operator, $amount, $interval) { if (!is_int($amount)) { throw new InvalidArgumentException('$amount parameter must be an integer'); } return sprintf('%s %s INTERVAL \'%s %s\'', $reference, $operator, $amount, $interval); } /** * Returns an expression using the specified operator. * * @param string $lhs The column or expression to test. * @param string $op The operator. * @param string $rhs The comparison value. * @param boolean $bind If true, the method returns the query and a list * of values suitable for binding as an array. * @param array $params Any additional parameters for the operator. * * @return string|array The SQL test fragment, or an array containing the * query and a list of values if $bind is true. */ public function buildClause($lhs, $op, $rhs, $bind = false, $params = array()) { $lhs = $this->_escapePrepare($lhs); switch ($op) { case '|': case '&': /* Only PgSQL 7.3+ understands SQL99 'SIMILAR TO'; use ~ for * greater backwards compatibility. */ $query = 'CASE WHEN CAST(%s AS VARCHAR) ~ \'^-?[0-9]+$\' THEN (CAST(%s AS INTEGER) %s %s) <> 0 ELSE FALSE END'; if ($bind) { return array(sprintf($query, $lhs, $lhs, $op, '?'), array((int)$rhs)); } else { return sprintf($query, $lhs, $lhs, $op, (int)$rhs); } case 'LIKE': $query = '%s ILIKE %s'; if ($bind) { if (empty($params['begin'])) { return array(sprintf($query, $lhs, '?'), array('%' . $rhs . '%')); } return array(sprintf('(' . $query . ' OR ' . $query . ')', $lhs, '?', $lhs, '?'), array($rhs . '%', '% ' . $rhs . '%')); } if (empty($params['begin'])) { return sprintf($query, $lhs, $this->_escapePrepare($this->quote('%' . $rhs . '%'))); } return sprintf('(' . $query . ' OR ' . $query . ')', $lhs, $this->_escapePrepare($this->quote($rhs . '%')), $lhs, $this->_escapePrepare($this->quote('% ' . $rhs . '%'))); } return parent::buildClause($lhs, $op, $rhs, $bind, $params); } /*########################################################################## # PostgreSQL specific methods ##########################################################################*/ /** * Returns the current database's encoding format. * * @return string The current database's encoding format. */ public function encoding() { return $this->selectValue( 'SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database WHERE pg_database.datname LIKE ' . $this->quote($this->currentDatabase())); } /** * Sets the schema search path to a string of comma-separated schema names. * * Names beginning with $ have to be quoted (e.g. $user => '$user'). See: * http://www.postgresql.org/docs/current/static/ddl-schemas.html * * @param string $schemaCsv A comma-separated schema name list. */ public function setSchemaSearchPath($schemaCsv) { if ($schemaCsv) { $this->execute('SET search_path TO ' . $schemaCsv); $this->_schemaSearchPath = $schemaCsv; } } /** * Returns the active schema search path. * * @return string The active schema search path. */ public function getSchemaSearchPath() { if (!$this->_schemaSearchPath) { $this->_schemaSearchPath = $this->selectValue('SHOW search_path'); } return $this->_schemaSearchPath; } /** * Returns the current client log message level. * * @return string The current client log message level. */ public function getClientMinMessages() { return $this->selectValue('SHOW client_min_messages'); } /** * Sets the client log message level. * * @param string $level The client log message level. One of DEBUG5, * DEBUG4, DEBUG3, DEBUG2, DEBUG1, LOG, NOTICE, * WARNING, ERROR, FATAL, or PANIC. */ public function setClientMinMessages($level) { return $this->execute('SET client_min_messages TO ' . $this->quote($level)); } /** * Returns the sequence name for a table's primary key or some other * specified key. * * If a sequence name doesn't exist, it is built from the table and primary * key name. * * @param string $tableName A table name. * @param string $pk A primary key name. Overrides the existing key * name when building a new sequence name. * * @return string The key's sequence name. */ public function defaultSequenceName($tableName, $pk = null) { list($defaultPk, $defaultSeq) = $this->pkAndSequenceFor($tableName); if (!$defaultSeq) { $defaultSeq = $tableName . '_' . ($pk ? $pk : ($defaultPk ? $defaultPk : 'id')) . '_seq'; } return $defaultSeq; } /** * Resets the sequence of a table's primary key to the maximum value. * * @param string $tableName A table name. * @param string $pk A primary key name. Defaults to the existing * primary key. * @param string $sequence A sequence name. Defaults to the sequence name * of the existing primary key. * * @return integer The (next) sequence value if a primary key and a * sequence exist. */ public function resetPkSequence($table, $pk = null, $sequence = null) { if (!$pk || !$sequence) { list($defaultPk, $defaultSequence) = $this->pkAndSequenceFor($table); if (!$pk) { $pk = $defaultPk; } if (!$sequence) { $sequence = $defaultSequence; } } if ($pk) { if ($sequence) { $quotedSequence = $this->quoteSequenceName($sequence); $quotedTable = $this->quoteTableName($table); $quotedPk = $this->quoteColumnName($pk); $sql = sprintf('SELECT setval(%s, (SELECT COALESCE(MAX(%s) + (SELECT increment_by FROM %s), (SELECT min_value FROM %s)) FROM %s), false)', $quotedSequence, $quotedPk, $sequence, $sequence, $quotedTable); $this->selectValue($sql, 'Reset sequence'); } else { if ($this->_logger) { $this->_logger->warn(sprintf('%s has primary key %s with no default sequence', $table, $pk)); } } } } /** * Returns a table's primary key and the key's sequence. * * @param string $tableName A table name. * * @return array Array with two values: the primary key name and the key's * sequence name. */ public function pkAndSequenceFor($table) { // First try looking for a sequence with a dependency on the // given table's primary key. $sql = " SELECT attr.attname, seq.relname FROM pg_class seq, pg_attribute attr, pg_depend dep, pg_namespace name, pg_constraint cons WHERE seq.oid = dep.objid AND seq.relkind = 'S' AND attr.attrelid = dep.refobjid AND attr.attnum = dep.refobjsubid AND attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1] AND cons.contype = 'p' AND dep.refobjid = '$table'::regclass"; $result = $this->selectOne($sql, 'PK and serial sequence'); if (!$result) { // If that fails, try parsing the primary key's default value. // Support the 7.x and 8.0 nextval('foo'::text) as well as // the 8.1+ nextval('foo'::regclass). $sql = " SELECT attr.attname, CASE WHEN split_part(def.adsrc, '''', 2) ~ '.' THEN substr(split_part(def.adsrc, '''', 2), strpos(split_part(def.adsrc, '''', 2), '.')+1) ELSE split_part(def.adsrc, '''', 2) END AS relname FROM pg_class t JOIN pg_attribute attr ON (t.oid = attrelid) JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum) JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1]) WHERE t.oid = '$table'::regclass AND cons.contype = 'p' AND def.adsrc ~* 'nextval'"; $result = $this->selectOne($sql, 'PK and custom sequence'); } // [primary_key, sequence] return array($result['attname'], $result['relname']); } /** * Returns the version of the connected PostgreSQL server. * * @return integer Zero padded PostgreSQL version, e.g. 80108 for 8.1.8. */ public function postgresqlVersion() { if (!$this->_version) { try { $version = $this->selectValue('SELECT version()'); if (preg_match('/PostgreSQL (\d+)\.(\d+)\.(\d+)/', $version, $matches)) $this->_version = ($matches[1] * 10000) + ($matches[2] * 100) + $matches[3]; } catch (Exception $e) { return 0; } } return $this->_version; } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Sqlite/Column.php0000664000175000017500000000455412205351473020224 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Sqlite_Column extends Horde_Db_Adapter_Base_Column { /** * @var array */ protected static $_hasEmptyStringDefault = array('binary', 'string', 'text'); public function extractDefault($default) { $default = parent::extractDefault($default); if ($this->isText()) { $default = $this->_unquote($default); } return $default; } /*########################################################################## # Type Juggling ##########################################################################*/ public function binaryToString($value) { return str_replace(array('%00', '%25'), array("\0", '%'), $value); } /** * @param mixed $value * @return boolean */ public function valueToBoolean($value) { if ($value == '"t"' || $value == "'t'") { return true; } elseif ($value == '""' || $value == "''") { return null; } else { return parent::valueToBoolean($value); } } /*########################################################################## # Protected ##########################################################################*/ /** * Unquote a string value * * @return string */ protected function _unquote($string) { $first = substr($string, 0, 1); if ($first == "'" || $first == '"') { $string = substr($string, 1); if (substr($string, -1) == $first) { $string = substr($string, 0, -1); } $string = str_replace("$first$first", $first, $string); } return $string; } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Sqlite/Schema.php0000664000175000017500000006457212205351473020175 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Sqlite_Schema extends Horde_Db_Adapter_Base_Schema { /*########################################################################## # Object factories ##########################################################################*/ /** * Factory for Column objects. * * @param string $name The column's name, such as "supplier_id" in * "supplier_id int(11)". * @param string $default The type-casted default value, such as "new" in * "sales_stage varchar(20) default 'new'". * @param string $sqlType Used to extract the column's type, length and * signed status, if necessary. For example * "varchar" and "60" in "company_name varchar(60)" * or "unsigned => true" in "int(10) UNSIGNED". * @param boolean $null Whether this column allows NULL values. * * @return Horde_Db_Adapter_Base_Column A column object. */ public function makeColumn($name, $default, $sqlType = null, $null = true) { return new Horde_Db_Adapter_Sqlite_Column($name, $default, $sqlType, $null); } /*########################################################################## # Quoting ##########################################################################*/ /** * Returns a quoted form of the column name. * * @param string $name A column name. * * @return string The quoted column name. */ public function quoteColumnName($name) { return '"' . str_replace('"', '""', $name) . '"'; } /** * Returns a quoted boolean true. * * @return string The quoted boolean true. */ public function quoteTrue() { return '1'; } /** * Returns a quoted boolean false. * * @return string The quoted boolean false. */ public function quoteFalse() { return '0'; } /** * Returns a quoted binary value. * * @param mixed A binary value. * * @return string The quoted binary value. */ public function quoteBinary($value) { return "'" . str_replace(array("'", '%', "\0"), array("''", '%25', '%00'), $value) . "'"; } /*########################################################################## # Schema Statements ##########################################################################*/ /** * Returns a hash of mappings from the abstract data types to the native * database types. * * See TableDefinition::column() for details on the recognized abstract * data types. * * @see TableDefinition::column() * * @return array A database type map. */ public function nativeDatabaseTypes() { return array( 'autoincrementKey' => $this->_defaultPrimaryKeyType(), 'string' => array('name' => 'varchar', 'limit' => 255), 'text' => array('name' => 'text', 'limit' => null), 'mediumtext' => array('name' => 'text', 'limit' => null), 'longtext' => array('name' => 'text', 'limit' => null), 'integer' => array('name' => 'int', 'limit' => null), 'float' => array('name' => 'float', 'limit' => null), 'decimal' => array('name' => 'decimal', 'limit' => null), 'datetime' => array('name' => 'datetime', 'limit' => null), 'timestamp' => array('name' => 'datetime', 'limit' => null), 'time' => array('name' => 'time', 'limit' => null), 'date' => array('name' => 'date', 'limit' => null), 'binary' => array('name' => 'blob', 'limit' => null), 'boolean' => array('name' => 'boolean', 'limit' => null), ); } /** * Returns a list of all tables of the current database. * * @return array A table list. */ public function tables() { return $this->selectValues("SELECT name FROM sqlite_master WHERE type = 'table' UNION ALL SELECT name FROM sqlite_temp_master WHERE type = 'table' AND name != 'sqlite_sequence' ORDER BY name"); } /** * Returns a table's primary key. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return Horde_Db_Adapter_Base_Index The primary key index object. */ public function primaryKey($tableName, $name = null) { // Share the columns cache with the columns() method $rows = @unserialize($this->_cache->get("tables/columns/$tableName", 0)); if (!$rows) { $rows = $this->selectAll('PRAGMA table_info(' . $this->quoteTableName($tableName) . ')', $name); $this->_cache->set("tables/columns/$tableName", serialize($rows)); } $pk = $this->makeIndex($tableName, 'PRIMARY', true, true, array()); foreach ($rows as $row) { if ($row['pk'] == 1) { $pk->columns[] = $row['name']; } } return $pk; } /** * Returns a list of tables indexes. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return array A list of Horde_Db_Adapter_Base_Index objects. */ public function indexes($tableName, $name = null) { $indexes = @unserialize($this->_cache->get("tables/indexes/$tableName", 0)); if (!$indexes) { $indexes = array(); foreach ($this->select('PRAGMA index_list(' . $this->quoteTableName($tableName) . ')') as $row) { if (strpos($row['name'], 'sqlite_') !== false) { // ignore internal sqlite_* index tables continue; } $index = $this->makeIndex( $tableName, $row['name'], false, (bool)$row['unique'], array()); foreach ($this->select('PRAGMA index_info(' . $this->quoteColumnName($index->name) . ')') as $field) { $index->columns[] = $field['name']; } $indexes[] = $index; } $this->_cache->set("tables/indexes/$tableName", serialize($indexes)); } return $indexes; } /** * Returns a list of table columns. * * @param string $tableName A table name. * @param string $name (can be removed?) * * @return array A list of Horde_Db_Adapter_Base_Column objects. */ public function columns($tableName, $name = null) { $rows = @unserialize($this->_cache->get("tables/columns/$tableName", 0)); if (!$rows) { $rows = $this->selectAll('PRAGMA table_info(' . $this->quoteTableName($tableName) . ')', $name); $this->_cache->set("tables/columns/$tableName", serialize($rows)); } // create columns from rows $columns = array(); foreach ($rows as $row) { $columns[$row['name']] = $this->makeColumn( $row['name'], $row['dflt_value'], $row['type'], !(bool)$row['notnull']); } return $columns; } /** * Renames a table. * * @param string $name A table name. * @param string $newName The new table name. */ public function renameTable($name, $newName) { $this->_clearTableCache($name); $sql = sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($name), $this->quoteTableName($newName)); return $this->execute($sql); } /** * Adds a new column to a table. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $type A data type. * @param array $options Column options. See * Horde_Db_Adapter_Base_TableDefinition#column() * for details. */ public function addColumn($tableName, $columnName, $type, $options=array()) { /* Ignore ':autoincrement' - it is handled automatically by SQLite * for any 'INTEGER PRIMARY KEY' column. */ if ($this->transactionStarted()) { throw new Horde_Db_Exception('Cannot add columns to a SQLite database while inside a transaction'); } parent::addColumn($tableName, $columnName, $type, $options); // See last paragraph on http://www.sqlite.org/lang_altertable.html $this->execute('VACUUM'); } /** * Removes a column from a table. * * @param string $tableName A table name. * @param string $columnName A column name. */ public function removeColumn($tableName, $columnName) { $this->_clearTableCache($tableName); return $this->_alterTable( $tableName, array(), create_function('$definition', 'unset($definition["' . $columnName . '"]);')); } /** * Changes an existing column's definition. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $type A data type. * @param array $options Column options. See * Horde_Db_Adapter_Base_TableDefinition#column() * for details. */ public function changeColumn($tableName, $columnName, $type, $options=array()) { $this->_clearTableCache($tableName); $defs = array(sprintf('$definition["%s"]->setType("%s"); if ("%s" == "autoincrementKey") $definition->primaryKey(false);', $columnName, $type, $type)); if (isset($options['limit'])) { $defs[] = sprintf('$definition["%s"]->setLimit("%s");', $columnName, $options['limit']); } if (isset($options['null'])) { $defs[] = sprintf('$definition["%s"]->setNull("%s");', $columnName, $options['null']); } if (isset($options['precision'])) { $defs[] = sprintf('$definition["%s"]->setPrecision("%s");', $columnName, $options['precision']); } if (isset($options['scale'])) { $defs[] = sprintf('$definition["%s"]->setScale("%s");', $columnName, $options['scale']); } if (array_key_exists('default', $options)) { if ($options['default'] === true) { $default = 'true'; } elseif ($options['default'] === false) { $default = 'false'; } elseif ($options['default'] === null) { $default = 'null'; } else { $default = '"' . $options['default'] . '"'; } $defs[] = sprintf('$definition["%s"]->setDefault(%s);', $columnName, $default); } return $this->_alterTable( $tableName, array(), create_function('$definition', implode("\n", $defs))); } /** * Sets a new default value for a column. * * If you want to set the default value to NULL, you are out of luck. You * need to execute the apppropriate SQL statement yourself. * * @param string $tableName A table name. * @param string $columnName A column name. * @param mixed $default The new default value. */ public function changeColumnDefault($tableName, $columnName, $default) { $this->_clearTableCache($tableName); $default = is_null($default) ? 'null' : '"' . $default . '"'; return $this->_alterTable( $tableName, array(), create_function('$definition', sprintf('$definition["%s"]->setDefault(%s);', $columnName, $default))); } /** * Renames a column. * * @param string $tableName A table name. * @param string $columnName A column name. * @param string $newColumnName The new column name. */ public function renameColumn($tableName, $columnName, $newColumnName) { $this->_clearTableCache($tableName); return $this->_alterTable( $tableName, array('rename' => array($columnName => $newColumnName))); } /** * Adds a primary key to a table. * * @param string $tableName A table name. * @param string|array $columnName One or more column names. * * @throws Horde_Db_Exception */ public function addPrimaryKey($tableName, $columns) { $this->_clearTableCache($tableName); $columns = (array)$columns; foreach ($columns as &$column) { $column = '"' . $column . '"'; } $callback = create_function( '$definition', sprintf('$definition->primaryKey(array(%s));', implode(', ', $columns))); $this->_alterTable($tableName, array(), $callback); } /** * Removes a primary key from a table. * * @param string $tableName A table name. * * @throws Horde_Db_Exception */ public function removePrimaryKey($tableName) { $this->_clearTableCache($tableName); $callback = create_function('$definition', '$definition->primaryKey(false);'); $this->_alterTable($tableName, array(), $callback); } /** * Removes an index from a table. * * See parent class for examples. * * @param string $tableName A table name. * @param string|array $options Either a column name or index options: * - name: (string) the index name. * - column: (string|array) column name(s). */ public function removeIndex($tableName, $options=array()) { $this->_clearTableCache($tableName); $index = $this->indexName($tableName, $options); $sql = 'DROP INDEX ' . $this->quoteColumnName($index); return $this->execute($sql); } /** * Creates a database. * * @param string $name A database name. * @param array $options Database options. */ public function createDatabase($name, $options = array()) { return new PDO('sqlite:' . $name); } /** * Drops a database. * * @param string $name A database name. */ public function dropDatabase($name) { if (!@file_exists($name)) { throw new Horde_Db_Exception('database does not exist'); } if (!@unlink($name)) { throw new Horde_Db_Exception('could not remove the database file'); } } /** * Returns the name of the currently selected database. * * @return string The database name. */ public function currentDatabase() { return $this->_config['dbname']; } /** * Generates a modified date for SELECT queries. * * @param string $reference The reference date - this is a column * referenced in the SELECT. * @param string $operator Add or subtract time? (+/-) * @param integer $amount The shift amount (number of days if $interval * is DAY, etc). * @param string $interval The interval (SECOND, MINUTE, HOUR, DAY, * MONTH, YEAR). * * @return string The generated INTERVAL clause. */ public function modifyDate($reference, $operator, $amount, $interval) { if (!is_int($amount)) { throw new InvalidArgumentException('$amount parameter must be an integer'); } switch ($interval) { case 'YEAR': $interval = 'years'; break; case 'MONTH': $interval = 'months'; break; case 'DAY': $interval = 'days'; break; case 'HOUR': $interval = 'hours'; break; case 'MINUTE': $interval = 'minutes'; break; case 'SECOND': $interval = 'seconds'; break; default: break; } return 'datetime(' . $reference . ', \'' . $operator . $amount . ' ' . $interval . '\')'; } /*########################################################################## # Protected ##########################################################################*/ /** * Returns a column type definition to be use for primary keys. * * @return string Primary key type definition. */ protected function _defaultPrimaryKeyType() { if ($this->supportsAutoIncrement()) { return 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'; } else { return 'INTEGER PRIMARY KEY NOT NULL'; } } /** * Alters a table. * * This is done by creating a temporary copy, applying changes and callback * methods, and copying the table back. * * @param string $tableName A table name. * @param array $options Any options to apply when creating the * temporary table. Supports a 'rename' key for * the new table name, additionally to the * options in createTable(). * @param function $callback A callback function that can manipulate the * Horde_Db_Adapter_Base_TableDefinition object * available in $definition. See _copyTable(). */ protected function _alterTable($tableName, $options = array(), $callback = null) { $this->beginDbTransaction(); $alteredTableName = 'altered_' . $tableName; $this->_moveTable($tableName, $alteredTableName, array_merge($options, array('temporary' => true))); $this->_moveTable($alteredTableName, $tableName, array(), $callback); $this->commitDbTransaction(); } /** * Moves a table. * * This is done by creating a temporary copy, applying changes and callback * methods, and dropping the original table. * * @param string $from The name of the source table. * @param string $to The name of the target table. * @param array $options Any options to apply when creating the * temporary table. Supports a 'rename' key for * the new table name, additionally to the * options in createTable(). * @param function $callback A callback function that can manipulate the * Horde_Db_Adapter_Base_TableDefinition object * available in $definition. See _copyTable(). */ protected function _moveTable($from, $to, $options = array(), $callback = null) { $this->_copyTable($from, $to, $options, $callback); $this->dropTable($from); } /** * Copies a table. * * Also applies changes and callback methods before creating the new table. * * @param string $from The name of the source table. * @param string $to The name of the target table. * @param array $options Any options to apply when creating the * temporary table. Supports a 'rename' key for * the new table name, additionally to the * options in createTable(). * @param function $callback A callback function that can manipulate the * Horde_Db_Adapter_Base_TableDefinition object * available in $definition. */ protected function _copyTable($from, $to, $options = array(), $callback = null) { $fromColumns = $this->columns($from); $pk = $this->primaryKey($from); if ($pk && count($pk->columns) == 1) { /* A primary key is not necessarily what matches the pseudo type * "autoincrementKey". We need to parse the table definition to * find out if the column is AUTOINCREMENT too. */ $tableDefinition = $this->selectValue('SELECT sql FROM sqlite_master WHERE name = ? UNION ALL SELECT sql FROM sqlite_temp_master WHERE name = ?', array($from, $from)); if (strpos($tableDefinition, $this->quoteColumnName($pk->columns[0]) . ' INTEGER PRIMARY KEY AUTOINCREMENT')) { $pkColumn = $pk->columns[0]; } else { $pkColumn = null; } } else { $pkColumn = null; } $options = array_merge($options, array('autoincrementKey' => false)); $copyPk = true; $definition = $this->createTable($to, $options); foreach ($fromColumns as $column) { $columnName = isset($options['rename'][$column->getName()]) ? $options['rename'][$column->getName()] : $column->getName(); $columnType = $column->getName() == $pkColumn ? 'autoincrementKey' : $column->getType(); if ($columnType == 'autoincrementKey') { $copyPk = false; } $definition->column($columnName, $columnType, array('limit' => $column->getLimit(), 'default' => $column->getDefault(), 'null' => $column->isNull())); } if ($pkColumn && count($pk->columns) && $copyPk) { $definition->primaryKey($pk->columns); } if (is_callable($callback)) { call_user_func($callback, $definition); } $definition->end(); $this->_copyTableIndexes( $from, $to, isset($options['rename']) ? $options['rename'] : array()); $this->_copyTableContents( $from, $to, array_map(create_function('$c', 'return $c->getName();'), iterator_to_array($definition)), isset($options['rename']) ? $options['rename'] : array()); } /** * Copies indexes from one table to another. * * @param string $from The name of the source table. * @param string $to The name of the target table. * @param array $rename A hash of columns to rename during the copy, with * original names as keys and the new names as values. */ protected function _copyTableIndexes($from, $to, $rename = array()) { $toColumnNames = array(); foreach ($this->columns($to) as $c) { $toColumnNames[$c->getName()] = true; } foreach ($this->indexes($from) as $index) { $name = $index->getName(); if ($to == 'altered_' . $from) { $name = 'temp_' . $name; } elseif ($from == 'altered_' . $to) { $name = substr($name, 5); } $columns = array(); foreach ($index->columns as $c) { if (isset($rename[$c])) { $c = $rename[$c]; } if (isset($toColumnNames[$c])) { $columns[] = $c; } } if (!empty($columns)) { // Index name can't be the same $opts = array('name' => str_replace('_' . $from . '_', '_' . $to . '_', $name)); if ($index->unique) { $opts['unique'] = true; } $this->addIndex($to, $columns, $opts); } } } /** * Copies the content of one table to another. * * @param string $from The name of the source table. * @param string $to The name of the target table. * @param array $columns A list of columns to copy. * @param array $rename A hash of columns to rename during the copy, with * original names as keys and the new names as * values. */ protected function _copyTableContents($from, $to, $columns, $rename = array()) { $columnMappings = array_combine($columns, $columns); foreach ($rename as $renameFrom => $renameTo) { $columnMappings[$renameTo] = $renameFrom; } $fromColumns = array(); foreach ($this->columns($from) as $col) { $fromColumns[] = $col->getName(); } $tmpColumns = array(); foreach ($columns as $col) { if (in_array($columnMappings[$col], $fromColumns)) { $tmpColumns[] = $col; } } $columns = $tmpColumns; $fromColumns = array(); foreach ($columns as $col) { $fromColumns[] = $columnMappings[$col]; } $quotedTo = $this->quoteTableName($to); $quotedToColumns = implode(', ', array_map(array($this, 'quoteColumnName'), $columns)); $quotedFrom = $this->quoteTableName($from); $quotedFromColumns = implode(', ', array_map(array($this, 'quoteColumnName'), $fromColumns)); $sql = sprintf('INSERT INTO %s (%s) SELECT %s FROM %s', $quotedTo, $quotedToColumns, $quotedFromColumns, $quotedFrom); $this->execute($sql); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Base.php0000664000175000017500000005433012205351473016375 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ abstract class Horde_Db_Adapter_Base implements Horde_Db_Adapter { /** * Config options. * * @var array */ protected $_config = array(); /** * DB connection. * * @var mixed */ protected $_connection = null; /** * Has a transaction been started? * * @var integer */ protected $_transactionStarted = 0; /** * The last query sent to the database. * * @var string */ protected $_lastQuery; /** * Row count of last action. * * @var integer */ protected $_rowCount = null; /** * Runtime of last query. * * @var integer */ protected $_runtime; /** * Is connection active? * * @var boolean */ protected $_active = null; /** * Cache object. * * @var Horde_Cache */ protected $_cache; /** * Log object. * * @var Horde_Log_Logger */ protected $_logger; /** * Schema object. * * @var Horde_Db_Adapter_Base_Schema */ protected $_schema = null; /** * Schema class to use. * * @var string */ protected $_schemaClass = null; /** * List of schema methods. * * @var array */ protected $_schemaMethods = array(); /*########################################################################## # Construct/Destruct ##########################################################################*/ /** * Constructor. * * @param array $config Configuration options and optional objects: *
     * 'charset' - (string) TODO
     * 
*/ public function __construct($config) { /* Can't set cache/logger in constructor - these objects may use DB * for storage. Add stubs for now - they have to be manually set * later with setCache() and setLogger(). */ $this->_cache = new Horde_Support_Stub(); $this->_logger = new Horde_Support_Stub(); // Default to UTF-8 if (!isset($config['charset'])) { $config['charset'] = 'UTF-8'; } $this->_config = $config; $this->_runtime = 0; if (!$this->_schemaClass) { $this->_schemaClass = __CLASS__ . '_Schema'; } $this->connect(); } /** * Free any resources that are open. */ public function __destruct() { $this->disconnect(); } /** * Serialize callback. */ public function __sleep() { return array_diff(array_keys(get_class_vars(__CLASS__)), array('_active', '_connection')); } /** * Unserialize callback. */ public function __wakeup() { $this->_schema->setAdapter($this); $this->connect(); } /** * Returns an adaptor option set through the constructor. * * @param string $option The option to return. * * @return mixed The option value or null if option doesn't exist or is * not set. */ public function getOption($option) { return isset($this->_config[$option]) ? $this->_config[$option] : null; } /*########################################################################## # Dependency setters/getters ##########################################################################*/ /** * Set a cache object. * * @inject * * @var Horde_Cache $logger The cache object. */ public function setCache(Horde_Cache $cache) { $this->_cache = $cache; } /** * @return Horde_Cache */ public function getCache() { return $this->_cache; } /** * Set a logger object. * * @inject * * @var Horde_Log_Logger $logger The logger object. */ public function setLogger(Horde_Log_Logger $logger) { $this->_logger = $logger; } /** * return Horde_Log_Logger */ public function getLogger() { return $this->_logger; } /*########################################################################## # Object composition ##########################################################################*/ /** * Delegate calls to the schema object. * * @param string $method * @param array $args * * @return mixed TODO * @throws BadMethodCallException */ public function __call($method, $args) { if (!$this->_schema) { // Create the database-specific (but not adapter specific) schema // object. $this->_schema = new $this->_schemaClass($this, array( 'cache' => $this->_cache, 'logger' => $this->_logger )); $this->_schemaMethods = array_flip(get_class_methods($this->_schema)); } if (isset($this->_schemaMethods[$method])) { return call_user_func_array(array($this->_schema, $method), $args); } $support = new Horde_Support_Backtrace(); $context = $support->getContext(1); $caller = $context['function']; if (isset($context['class'])) { $caller = $context['class'] . '::' . $caller; } throw new BadMethodCallException('Call to undeclared method "' . get_class($this) . '::' . $method . '" from "' . $caller . '"'); } /*########################################################################## # Public ##########################################################################*/ /** * Returns the human-readable name of the adapter. Use mixed case - one * can always use downcase if needed. * * @return string */ public function adapterName() { return 'Base'; } /** * Does this adapter support migrations? Backend specific, as the * abstract adapter always returns +false+. * * @return boolean */ public function supportsMigrations() { return false; } /** * Does this adapter support using DISTINCT within COUNT? This is +true+ * for all adapters except sqlite. * * @return boolean */ public function supportsCountDistinct() { return true; } /** * Should primary key values be selected from their corresponding * sequence before the insert statement? If true, next_sequence_value * is called before each insert to set the record's primary key. * This is false for all adapters but Firebird. * * @return boolean */ public function prefetchPrimaryKey($tableName = null) { return false; } /** * Get the last query run * * @return string */ public function getLastQuery() { return $this->_lastQuery; } /** * Reset the timer * * @return integer */ public function resetRuntime() { $this->_runtime = 0; return $this->_runtime; } /*########################################################################## # Connection Management ##########################################################################*/ /** * Is the connection active? * * @return boolean */ public function isActive() { return $this->_active && $this->_connection; } /** * Reconnect to the db. */ public function reconnect() { $this->disconnect(); $this->connect(); } /** * Disconnect from db. */ public function disconnect() { $this->_connection = null; $this->_active = false; } /** * Provides access to the underlying database connection. Useful for when * you need to call a proprietary method such as postgresql's * lo_* methods. * * @return resource */ public function rawConnection() { return $this->_connection; } /*########################################################################## # Database Statements ##########################################################################*/ /** * Returns an array of records with the column names as keys, and * column values as values. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return PDOStatement * @throws Horde_Db_Exception */ public function select($sql, $arg1 = null, $arg2 = null) { return $this->execute($sql, $arg1, $arg2); } /** * Returns an array of record hashes with the column names as keys and * column values as values. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectAll($sql, $arg1 = null, $arg2 = null) { $rows = array(); $result = $this->select($sql, $arg1, $arg2); if ($result) { foreach ($result as $row) { $rows[] = $row; } } return $rows; } /** * Returns a record hash with the column names as keys and column values * as values. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectOne($sql, $arg1 = null, $arg2 = null) { $result = $this->selectAll($sql, $arg1, $arg2); return $result ? next($result) : array(); } /** * Returns a single value from a record * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return string * @throws Horde_Db_Exception */ public function selectValue($sql, $arg1 = null, $arg2 = null) { $result = $this->selectOne($sql, $arg1, $arg2); return $result ? next($result) : null; } /** * Returns an array of the values of the first column in a select: * selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3] * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectValues($sql, $arg1 = null, $arg2 = null) { $result = $this->selectAll($sql, $arg1, $arg2); $values = array(); foreach ($result as $row) { $values[] = next($row); } return $values; } /** * Returns an array where the keys are the first column of a select, and the * values are the second column: * * selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler'] * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectAssoc($sql, $arg1 = null, $arg2 = null) { $result = $this->selectAll($sql, $arg1, $arg2); $values = array(); foreach ($result as $row) { $values[current($row)] = next($row); } return $values; } /** * Executes the SQL statement in the context of this connection. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return PDOStatement * @throws Horde_Db_Exception */ public function execute($sql, $arg1 = null, $arg2 = null) { if (!$this->isActive()) { $this->reconnect(); } if (is_array($arg1)) { $sql = $this->_replaceParameters($sql, $arg1); $name = $arg2; } else { $name = $arg1; } $t = new Horde_Support_Timer; $t->push(); try { $this->_lastQuery = $sql; $stmt = $this->_connection->query($sql); } catch (Exception $e) { $this->_logError($sql, 'QUERY FAILED: ' . $e->getMessage()); $this->_logInfo($sql, $name); throw new Horde_Db_Exception($e); } $this->_logInfo($sql, $name, $t->pop()); $this->_rowCount = $stmt ? $stmt->rowCount() : 0; return $stmt; } /** * Inserts a row into a table. * * @param string $sql SQL statement. * @param array|string $arg1 Either an array of bound parameters or a * query name. * @param string $arg2 If $arg1 contains bound parameters, the * query name. * @param string $pk The primary key column. * @param integer $idValue The primary key value. This parameter is * required if the primary key is inserted * manually. * @param string $sequenceName The sequence name. * * @return integer Last inserted ID. * @throws Horde_Db_Exception */ public function insert($sql, $arg1 = null, $arg2 = null, $pk = null, $idValue = null, $sequenceName = null) { $this->execute($sql, $arg1, $arg2); return $idValue ? $idValue : $this->_connection->lastInsertId($sequenceName); } /** * Executes the update statement and returns the number of rows affected. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return integer Number of rows affected. * @throws Horde_Db_Exception */ public function update($sql, $arg1 = null, $arg2 = null) { $this->execute($sql, $arg1, $arg2); return $this->_rowCount; } /** * Executes the delete statement and returns the number of rows affected. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return integer Number of rows affected. * @throws Horde_Db_Exception */ public function delete($sql, $arg1 = null, $arg2 = null) { $this->execute($sql, $arg1, $arg2); return $this->_rowCount; } /** * Check if a transaction has been started. * * @return boolean True if transaction has been started. */ public function transactionStarted() { return (bool)$this->_transactionStarted; } /** * Begins the transaction (and turns off auto-committing). */ public function beginDbTransaction() { if (!$this->_transactionStarted) { $this->_connection->beginTransaction(); } $this->_transactionStarted++; } /** * Commits the transaction (and turns on auto-committing). */ public function commitDbTransaction() { $this->_transactionStarted--; if (!$this->_transactionStarted) { $this->_connection->commit(); } } /** * Rolls back the transaction (and turns on auto-committing). Must be * done if the transaction block raises an exception or returns false. */ public function rollbackDbTransaction() { if (!$this->_transactionStarted) { return; } $this->_connection->rollBack(); $this->_transactionStarted = 0; } /** * Appends LIMIT and OFFSET options to a SQL statement. * * @param string $sql SQL statement. * @param array $options Hash with 'limit' and (optional) 'offset' values. * * @return string */ public function addLimitOffset($sql, $options) { if (isset($options['limit']) && $limit = $options['limit']) { if (isset($options['offset']) && $offset = $options['offset']) { $sql .= " LIMIT $offset, $limit"; } else { $sql .= " LIMIT $limit"; } } return $sql; } /** * TODO */ public function sanitizeLimit($limit) { return (strpos($limit, ',') !== false) ? implode(',', array_map('intval', explode(',', $limit))) : intval($limit); } /** * Appends a locking clause to an SQL statement. * This method *modifies* the +sql+ parameter. * * # SELECT * FROM suppliers FOR UPDATE * add_lock! 'SELECT * FROM suppliers', :lock => true * add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE' * * @param string &$sql SQL statment. * @param array $options TODO. */ public function addLock(&$sql, array $options = array()) { $sql .= (isset($options['lock']) && is_string($options['lock'])) ? ' ' . $options['lock'] : ' FOR UPDATE'; } /** * Inserts the given fixture into the table. Overridden in adapters that * require something beyond a simple insert (eg. Oracle). * * @param TODO $fixture TODO * @param TODO $tableName TODO * * @return TODO */ public function insertFixture($fixture, $tableName) { /*@TODO*/ return $this->execute("INSERT INTO #{quote_table_name(table_name)} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'); } /** * TODO * * @param string $tableName TODO * * @return string TODO */ public function emptyInsertStatement($tableName) { return 'INSERT INTO ' . $this->quoteTableName($tableName) . ' VALUES(DEFAULT)'; } /*########################################################################## # Protected ##########################################################################*/ /** * Checks if required configuration keys are present. * * @param array $required Required configuration keys. * * @throws Horde_Db_Exception if a required key is missing. */ protected function _checkRequiredConfig(array $required) { $diff = array_diff_key(array_flip($required), $this->_config); if (!empty($diff)) { $msg = 'Required config missing: ' . implode(', ', array_keys($diff)); throw new Horde_Db_Exception($msg); } } /** * Replace ? in a SQL statement with quoted values from $args * * @param string $sql SQL statement. * @param array $args TODO * * @return string Modified SQL statement. * @throws Horde_Db_Exception */ protected function _replaceParameters($sql, array $args) { $paramCount = substr_count($sql, '?'); if (count($args) != $paramCount) { $this->_logError('Parameter count mismatch: ' . $sql, 'Horde_Db_Adapter_Base::_replaceParameters'); throw new Horde_Db_Exception(sprintf('Parameter count mismatch, expecting %d, got %d', $paramCount, count($args))); } $sqlPieces = explode('?', $sql); $sql = array_shift($sqlPieces); while (count($sqlPieces)) { $sql .= $this->quote(array_shift($args)) . array_shift($sqlPieces); } return $sql; } /** * Logs the SQL query for debugging. * * @param string $sql SQL statement. * @param string $name TODO * @param float $runtime Runtime interval. */ protected function _logInfo($sql, $name, $runtime = null) { if ($this->_logger) { $name = (empty($name) ? '' : $name) . (empty($runtime) ? '' : sprintf(" (%.4fs)", $runtime)); $this->_logger->debug($this->_formatLogEntry($name, $sql)); } } protected function _logError($error, $name, $runtime = null) { if ($this->_logger) { $name = (empty($name) ? '' : $name) . (empty($runtime) ? '' : sprintf(" (%.4fs)", $runtime)); $this->_logger->err($this->_formatLogEntry($name, $error)); } } /** * Formats the log entry. * * @param string $message Message. * @param string $sql SQL statment. * * @return string Formatted log entry. */ protected function _formatLogEntry($message, $sql) { return "SQL $message \n\t" . wordwrap(preg_replace("/\s+/", ' ', $sql), 70, "\n\t ", 1); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Mysql.php0000664000175000017500000002761212205351473016633 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * MySQL Horde_Db_Adapter * * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Mysql extends Horde_Db_Adapter_Base { /** * Mysql database connection handle. * @var resource */ protected $_connection = null; /** * Last auto-generated insert_id * @var integer */ protected $_insertId; /** * @var string */ protected $_schemaClass = 'Horde_Db_Adapter_Mysql_Schema'; /*########################################################################## # Public ##########################################################################*/ /** * Returns the human-readable name of the adapter. Use mixed case - one * can always use downcase if needed. * * @return string */ public function adapterName() { return 'MySQL'; } /** * Does this adapter support migrations? Backend specific, as the * abstract adapter always returns +false+. * * @return boolean */ public function supportsMigrations() { return true; } /*########################################################################## # Connection Management ##########################################################################*/ /** * Connect to the db */ public function connect() { if ($this->_active) { return; } $config = $this->_parseConfig(); $oldTrackErrors = ini_set('track_errors', 1); $mysql = @mysql_connect($config['host'], $config['username'], $config['password']); ini_set('track_errors', $oldTrackErrors); if (!$mysql) { throw new Horde_Db_Exception('Connect failed: ' . $php_errormsg); } if (!mysql_select_db($config['dbname'])) { throw new Horde_Db_Exception('Could not select database: ' . $config['dbname']); } $this->_connection = $mysql; $this->_active = true; // Set the default charset. http://dev.mysql.com/doc/refman/5.1/en/charset-connection.html if (!empty($config['charset'])) { $this->setCharset($config['charset']); } } /** * Disconnect from db */ public function disconnect() { if ($this->_connection) { @mysql_close($this->_connection); } $this->_connection = null; $this->_active = false; } /** * Check if the connection is active * * @return boolean */ public function isActive() { return isset($this->_connection) && @mysql_ping($this->_connection); } /*########################################################################## # Quoting ##########################################################################*/ /** * Quotes a string, escaping any ' (single quote) and \ (backslash) * characters.. * * @param string $string * @return string */ public function quoteString($string) { return "'" . mysql_real_escape_string($string, $this->_connection) . "'"; } /*########################################################################## # Database Statements ##########################################################################*/ /** * Returns an array of records with the column names as keys, and * column values as values. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * @return array */ public function select($sql, $arg1 = null, $arg2 = null) { return new Horde_Db_Adapter_Mysql_Result($this, $sql, $arg1, $arg2); } /** * Returns an array of record hashes with the column names as keys and * column values as values. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function selectAll($sql, $arg1 = null, $arg2 = null) { $result = $this->execute($sql, $arg1, $arg2); $rows = array(); if ($result) { while ($row = mysql_fetch_array($result, MYSQL_ASSOC)) { $rows[] = $row; } } return $rows; } /** * Returns a record hash with the column names as keys and column values as * values. * * @param string $sql A query. * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * * @return array|boolean A record hash or false if no record found. */ public function selectOne($sql, $arg1 = null, $arg2 = null) { $result = $this->execute($sql, $arg1, $arg2); return $result ? mysql_fetch_array($result, MYSQL_ASSOC) : array(); } /** * Returns a single value from a record * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * @return string */ public function selectValue($sql, $arg1=null, $arg2=null) { $result = $this->selectOne($sql, $arg1, $arg2); return $result ? current($result) : null; } /** * Returns an array of the values of the first column in a select: * select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function selectValues($sql, $arg1=null, $arg2=null) { $values = array(); $result = $this->execute($sql, $arg1, $arg2); if ($result) { while ($row = mysql_fetch_row($result)) { $values[] = $row[0]; } } return $values; } /** * Executes the SQL statement in the context of this connection. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function execute($sql, $arg1=null, $arg2=null) { if (is_array($arg1)) { $sql = $this->_replaceParameters($sql, $arg1); $name = $arg2; } else { $name = $arg1; } $t = new Horde_Support_Timer(); $t->push(); $this->_lastQuery = $sql; $stmt = mysql_query($sql, $this->_connection); if (!$stmt) { $this->_logInfo($sql, 'QUERY FAILED: ' . mysql_error($this->_connection)); $this->_logInfo($sql, $name); throw new Horde_Db_Exception('QUERY FAILED: ' . mysql_error($this->_connection) . "\n\n" . $sql, $this->_errorCode(null, mysql_errno($this->_connection))); } $this->_logInfo($sql, $name, $t->pop()); $this->_rowCount = mysql_affected_rows($this->_connection); $this->_insertId = mysql_insert_id($this->_connection); return $stmt; } /** * Returns the last auto-generated ID from the affected table. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * @param string $pk * @param int $idValue * @param string $sequenceName */ public function insert($sql, $arg1 = null, $arg2 = null, $pk = null, $idValue = null, $sequenceName = null) { $this->execute($sql, $arg1, $arg2); return isset($idValue) ? $idValue : $this->_insertId; } /** * Begins the transaction (and turns off auto-committing). */ public function beginDbTransaction() { $this->_transactionStarted++; $this->_lastQuery = 'SET AUTOCOMMIT=0; BEGIN'; @mysql_query('SET AUTOCOMMIT=0', $this->_connection) && @mysql_query('BEGIN', $this->_connection); } /** * Commits the transaction (and turns on auto-committing). */ public function commitDbTransaction() { $this->_transactionStarted--; if (!$this->_transactionStarted) { $this->_lastQuery = 'COMMIT; SET AUTOCOMMIT=1'; @mysql_query('COMMIT', $this->_connection) && @mysql_query('SET AUTOCOMMIT=1', $this->_connection); } } /** * Rolls back the transaction (and turns on auto-committing). Must be * done if the transaction block raises an exception or returns false. */ public function rollbackDbTransaction() { if (!$this->_transactionStarted) { return; } $this->_lastQuery = 'ROLLBACK; SET AUTOCOMMIT=1'; @mysql_query('ROLLBACK', $this->_connection) && @mysql_query('SET AUTOCOMMIT=1', $this->_connection); $this->_transactionStarted = 0; } /*########################################################################## # Protected ##########################################################################*/ /** * Return a standard error code * * @param string $sqlstate * @param integer $errno * @return integer */ protected function _errorCode($sqlstate, $errno) { /*@TODO do something with standard sqlstate vs. MySQL error codes vs. whatever else*/ return $errno; } /** * Parse configuration array into options for mysql_connect * * @throws Horde_Db_Exception * @return array [host, username, password, dbname] */ protected function _parseConfig() { $this->_checkRequiredConfig(array('username')); $rails2mysqli = array('database' => 'dbname'); foreach ($rails2mysqli as $from => $to) { if (isset($this->_config[$from])) { $this->_config[$to] = $this->_config[$from]; unset($this->_config[$from]); } } if (!empty($this->_config['host']) && $this->_config['host'] == 'localhost') { $this->_config['host'] = '127.0.0.1'; } if (isset($this->_config['port'])) { if (empty($this->_config['host'])) { throw new Horde_Db_Exception('Host is required if port is specified'); } $this->_config['host'] .= ':' . $this->_config['port']; unset($this->_config['port']); } if (!empty($this->_config['socket'])) { if (!empty($this->_config['host'])) { throw new Horde_Db_Exception('Can only specify host or socket, not both'); } $this->_config['host'] = ':' . $this->_config['socket']; unset($this->_config['socket']); } $config = $this->_config; if (!isset($config['host'])) $config['host'] = null; if (!isset($config['username'])) $config['username'] = null; if (!isset($config['password'])) $config['password'] = null; if (!isset($config['dbname'])) $config['dbname'] = null; return $config; } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/Mysqli.php0000664000175000017500000003332412205351473017001 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * MySQL Improved Horde_Db_Adapter * * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Mysqli extends Horde_Db_Adapter_Base { /** * Mysqli database connection object. * @var mysqli */ protected $_connection = null; /** * Last auto-generated insert_id * @var integer */ protected $_insertId; /** * @var string */ protected $_schemaClass = 'Horde_Db_Adapter_Mysql_Schema'; /** * @var boolean */ protected $_hasMysqliFetchAll = false; /*########################################################################## # Public ##########################################################################*/ /** * Returns the human-readable name of the adapter. Use mixed case - one * can always use downcase if needed. * * @return string */ public function adapterName() { return 'MySQLi'; } /** * Does this adapter support migrations? Backend specific, as the * abstract adapter always returns +false+. * * @return boolean */ public function supportsMigrations() { return true; } /*########################################################################## # Connection Management ##########################################################################*/ /** * Connect to the db * * MySQLi can connect using SSL if $config contains an 'ssl' sub-array * containing the following keys: * + key The path to the key file. * + cert The path to the certificate file. * + ca The path to the certificate authority file. * + capath The path to a directory that contains trusted SSL * CA certificates in pem format. * + cipher The list of allowable ciphers for SSL encryption. * * Example of how to connect using SSL: * * $config = array( * 'username' => 'someuser', * 'password' => 'apasswd', * 'hostspec' => 'localhost', * 'database' => 'thedb', * 'ssl' => array( * 'key' => 'client-key.pem', * 'cert' => 'client-cert.pem', * 'ca' => 'cacert.pem', * 'capath' => '/path/to/ca/dir', * 'cipher' => 'AES', * ), * ); * * $db = new Horde_Db_Adapter_Mysqli($config); * */ public function connect() { if ($this->_active) { return; } $config = $this->_parseConfig(); if (!empty($config['ssl'])) { $mysqli = mysqli_init(); $mysqli->ssl_set( empty($config['ssl']['key']) ? null : $config['ssl']['key'], empty($config['ssl']['cert']) ? null : $config['ssl']['cert'], empty($config['ssl']['ca']) ? null : $config['ssl']['ca'], empty($config['ssl']['capath']) ? null : $config['ssl']['capath'], empty($config['ssl']['cipher']) ? null : $config['ssl']['cipher'] ); $mysqli->real_connect( $config['host'], $config['username'], $config['password'], $config['dbname'], $config['port'], $config['socket']); } else { $mysqli = new mysqli( $config['host'], $config['username'], $config['password'], $config['dbname'], $config['port'], $config['socket']); } if (mysqli_connect_errno()) { throw new Horde_Db_Exception('Connect failed: (' . mysqli_connect_errno() . ') ' . mysqli_connect_error(), mysqli_connect_errno()); } // If supported, request real datatypes from MySQL instead of returning // everything as a string. if (defined('MYSQLI_OPT_INT_AND_FLOAT_NATIVE')) { $mysqli->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, true); } $this->_connection = $mysqli; $this->_active = true; // Set the default charset. http://dev.mysql.com/doc/refman/5.1/en/charset-connection.html if (!empty($config['charset'])) { $this->setCharset($config['charset']); } $this->_hasMysqliFetchAll = function_exists('mysqli_fetch_all'); } /** * Disconnect from db */ public function disconnect() { if ($this->_connection) { $this->_connection->close(); } $this->_connection = null; $this->_active = false; } /** * Check if the connection is active * * @return boolean */ public function isActive() { $this->_lastQuery = 'SELECT 1'; return isset($this->_connection) && $this->_connection->query('SELECT 1'); } /*########################################################################## # Quoting ##########################################################################*/ /** * Quotes a string, escaping any ' (single quote) and \ (backslash) * characters.. * * @param string $string * @return string */ public function quoteString($string) { return "'".$this->_connection->real_escape_string($string)."'"; } /*########################################################################## # Database Statements ##########################################################################*/ /** * Returns an array of records with the column names as keys, and * column values as values. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * @return array */ public function select($sql, $arg1=null, $arg2=null) { return new Horde_Db_Adapter_Mysqli_Result($this, $sql, $arg1, $arg2); } /** * Returns an array of record hashes with the column names as keys and * column values as values. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function selectAll($sql, $arg1=null, $arg2=null) { $result = $this->execute($sql, $arg1, $arg2); if ($this->_hasMysqliFetchAll) { return $result->fetch_all(MYSQLI_ASSOC); } else { $rows = array(); if ($result) { while ($row = $result->fetch_array(MYSQLI_ASSOC)) { $rows[] = $row; } } } return $rows; } /** * Returns a record hash with the column names as keys and column values as * values. * * @param string $sql A query. * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * * @return array|boolean A record hash or false if no record found. */ public function selectOne($sql, $arg1 = null, $arg2 = null) { $result = $this->execute($sql, $arg1, $arg2); $result = $result ? $result->fetch_array(MYSQLI_ASSOC) : array(); return is_null($result) ? false : $result; } /** * Returns a single value from a record * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * @return string */ public function selectValue($sql, $arg1=null, $arg2=null) { $result = $this->selectOne($sql, $arg1, $arg2); return $result ? current($result) : null; } /** * Returns an array of the values of the first column in a select: * select_values("SELECT id FROM companies LIMIT 3") => [1,2,3] * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function selectValues($sql, $arg1=null, $arg2=null) { $values = array(); $result = $this->execute($sql, $arg1, $arg2); if ($result) { while ($row = $result->fetch_row()) { $values[] = $row[0]; } } return $values; } /** * Executes the SQL statement in the context of this connection. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. */ public function execute($sql, $arg1=null, $arg2=null) { if (is_array($arg1)) { $sql = $this->_replaceParameters($sql, $arg1); $name = $arg2; } else { $name = $arg1; } $t = new Horde_Support_Timer(); $t->push(); $this->_lastQuery = $sql; $stmt = $this->_connection->query($sql); if (!$stmt) { $this->_logInfo($sql, 'QUERY FAILED: ' . $this->_connection->error); $this->_logInfo($sql, $name); throw new Horde_Db_Exception('QUERY FAILED: ' . $this->_connection->error . "\n\n" . $sql, $this->_errorCode($this->_connection->sqlstate, $this->_connection->errno)); } $this->_logInfo($sql, $name, $t->pop()); //@TODO if ($this->_connection->info) $this->_loginfo($sql, $this->_connection->info); //@TODO also log warnings? http://php.net/mysqli.warning-count and http://php.net/mysqli.get-warnings $this->_rowCount = $this->_connection->affected_rows; $this->_insertId = $this->_connection->insert_id; return $stmt; } /** * Returns the last auto-generated ID from the affected table. * * @param string $sql * @param mixed $arg1 Either an array of bound parameters or a query name. * @param string $arg2 If $arg1 contains bound parameters, the query name. * @param string $pk * @param int $idValue * @param string $sequenceName */ public function insert($sql, $arg1=null, $arg2=null, $pk=null, $idValue=null, $sequenceName=null) { $this->execute($sql, $arg1, $arg2); return isset($idValue) ? $idValue : $this->_insertId; } /** * Begins the transaction (and turns off auto-committing). */ public function beginDbTransaction() { $this->_connection->autocommit(false); $this->_transactionStarted++; } /** * Commits the transaction (and turns on auto-committing). */ public function commitDbTransaction() { parent::commitDbTransaction(); if (!$this->_transactionStarted) { $this->_connection->autocommit(true); } } /** * Rolls back the transaction (and turns on auto-committing). Must be * done if the transaction block raises an exception or returns false. */ public function rollbackDbTransaction() { parent::rollbackDbTransaction(); if (!$this->_transactionStarted) { $this->_connection->autocommit(true); } } /*########################################################################## # Protected ##########################################################################*/ /** * Return a standard error code * * @param string $sqlstate * @param integer $errno * @return integer */ protected function _errorCode($sqlstate, $errno) { /*@TODO do something with standard sqlstate vs. MySQL error codes vs. whatever else*/ return $errno; } /** * Parse configuration array into options for MySQLi constructor. * * @throws Horde_Db_Exception * @return array [host, username, password, dbname, port, socket] */ protected function _parseConfig() { $this->_checkRequiredConfig(array('username')); $rails2mysqli = array('database' => 'dbname'); foreach ($rails2mysqli as $from => $to) { if (isset($this->_config[$from])) { $this->_config[$to] = $this->_config[$from]; unset($this->_config[$from]); } } if (!empty($this->_config['host']) && $this->_config['host'] == 'localhost') { $this->_config['host'] = '127.0.0.1'; } if (isset($this->_config['port'])) { if (empty($this->_config['host'])) { throw new Horde_Db_Exception('Host is required if port is specified'); } } $config = $this->_config; if (!isset($config['host'])) $config['host'] = null; if (!isset($config['username'])) $config['username'] = null; if (!isset($config['password'])) $config['password'] = null; if (!isset($config['dbname'])) $config['dbname'] = null; if (!isset($config['port'])) $config['port'] = null; if (!isset($config['socket'])) $config['socket'] = null; return $config; } } Horde_Db-2.0.4/lib/Horde/Db/Adapter/SplitRead.php0000664000175000017500000003473412205351473017420 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Michael J. Rubinsky * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * The Horde_Db_Adapter_SplitRead:: class wraps two individual adapters to * provide support for split read/write database setups. * * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @author Michael J. Rubinsky * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_SplitRead implements Horde_Db_Adapter { /** * The read adapter * * @var Horde_Db_Adapter */ private $_read; /** * The write adapter * * @var Horde_Db_Adapter */ private $_write; /** * Const'r * * @param Horde_Db_Adapter $read * @param Horde_Db_Adapter $write */ public function __construct(Horde_Db_Adapter $read, Horde_Db_Adapter $write) { $this->_read = $read; $this->_write = $write; } /** * Delegate unknown methods to the _write adapter. * * @param string $method * @param array $args */ public function __call($method, $args) { $result = call_user_func_array(array($this->_write, $method), $args); $this->_lastQuery = $this->_write->getLastQuery(); return $result; } /** * Returns the human-readable name of the adapter. Use mixed case - one * can always use downcase if needed. * * @return string */ public function adapterName() { return 'SplitRead'; } /** * Does this adapter support migrations? * * @return boolean */ public function supportsMigrations() { return $this->_write->supportsMigrations(); } /** * Does this adapter support using DISTINCT within COUNT? This is +true+ * for all adapters except sqlite. * * @return boolean */ public function supportsCountDistinct() { return $this->_read->supportsCountDistinct(); } /** * Should primary key values be selected from their corresponding * sequence before the insert statement? If true, next_sequence_value * is called before each insert to set the record's primary key. * This is false for all adapters but Firebird. * * @return boolean */ public function prefetchPrimaryKey($tableName = null) { return $this->_write->prefetchPrimaryKey($tableName); } /*########################################################################## # Connection Management ##########################################################################*/ /** * Connect to the db. * @TODO: Lazy connect? * */ public function connect() { $this->_write->connect(); $this->_read->connect(); } /** * Is the connection active? * * @return boolean */ public function isActive() { return ($this->_read->isActive() && $this->_write->isActive()); } /** * Reconnect to the db. */ public function reconnect() { $this->disconnect(); $this->connect(); } /** * Disconnect from db. */ public function disconnect() { $this->_read->disconnect(); $this->_write->disconnect(); } /** * Provides access to the underlying database connection. Useful for when * you need to call a proprietary method such as postgresql's * lo_* methods. * * @return resource */ public function rawConnection() { return $this->_write->rawConnection(); } /*########################################################################## # Quoting ##########################################################################*/ /** * Quotes a string, escaping any special characters. * * @param string $string * @return string */ public function quoteString($string) { return $this->_read->quoteString($string); } /*########################################################################## # Database Statements ##########################################################################*/ /** * Returns an array of records with the column names as keys, and * column values as values. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return PDOStatement * @throws Horde_Db_Exception */ public function select($sql, $arg1 = null, $arg2 = null) { $result = $this->_read->select($sql, $arg1, $arg2); $this->_lastQuery = $this->_read->getLastQuery(); return $result; } /** * Returns an array of record hashes with the column names as keys and * column values as values. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectAll($sql, $arg1 = null, $arg2 = null) { $result = $this->_read->selectAll($sql, $arg1, $arg2); $this->_lastQuery = $this->_read->getLastQuery(); return $result; } /** * Returns a record hash with the column names as keys and column values * as values. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectOne($sql, $arg1 = null, $arg2 = null) { $result = $this->_read->selectOne($sql, $arg1, $arg2); $this->_lastQuery = $this->_read->getLastQuery(); return $result; } /** * Returns a single value from a record * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return string * @throws Horde_Db_Exception */ public function selectValue($sql, $arg1 = null, $arg2 = null) { $result = $this->_read->selectValue($sql, $arg1, $arg2); $this->_lastQuery = $this->_read->getLastQuery(); return $result; } /** * Returns an array of the values of the first column in a select: * selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3] * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectValues($sql, $arg1 = null, $arg2 = null) { $result = $this->_read->selectValues($sql, $arg1, $arg2); $this->_lastQuery = $this->_read->getLastQuery(); return $result; } /** * Returns an array where the keys are the first column of a select, and the * values are the second column: * * selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler'] * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectAssoc($sql, $arg1 = null, $arg2 = null) { $result = $this->_read->selectAssoc($sql, $arg1, $arg2); $this->_lastQuery = $this->_read->getLastQuery(); return $result; } /** * Executes the SQL statement in the context of this connection. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return PDOStatement * @throws Horde_Db_Exception */ public function execute($sql, $arg1 = null, $arg2 = null) { // Can't assume this will always be a read action, use _write. $result = $this->_write->execute($sql, $arg1, $arg2); $this->_lastQuery = $this->_write->getLastQuery(); // Once doing writes, keep using the write backend even for reads // at least during the same request, to help against stale data. $this->_read = $this->_write; return $result; } /** * Returns the last auto-generated ID from the affected table. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a * query name. * @param string $arg2 If $arg1 contains bound parameters, the * query name. * @param string $pk TODO * @param integer $idValue TODO * @param string $sequenceName TODO * * @return integer Last inserted ID. * @throws Horde_Db_Exception */ public function insert($sql, $arg1 = null, $arg2 = null, $pk = null, $idValue = null, $sequenceName = null) { $result = $this->_write->insert($sql, $arg1, $arg2, $pk, $idValue, $sequenceName); $this->_lastQuery = $this->_write->getLastQuery(); // Once doing writes, keep using the write backend even for reads // at least during the same request, to help against stale data. $this->_read = $this->_write; return $result; } /** * Executes the update statement and returns the number of rows affected. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return integer Number of rows affected. * @throws Horde_Db_Exception */ public function update($sql, $arg1 = null, $arg2 = null) { $result = $this->_write->update($sql, $arg1, $arg2); $this->_lastQuery = $this->_write->getLastQuery(); // Once doing writes, keep using the write backend even for reads // at least during the same request, to help against stale data. $this->_read = $this->_write; return $result; } /** * Executes the delete statement and returns the number of rows affected. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return integer Number of rows affected. * @throws Horde_Db_Exception */ public function delete($sql, $arg1 = null, $arg2 = null) { $result = $this->_write->delete($sql, $arg1, $arg2); $this->_lastQuery = $this->_write->getLastQuery(); // Once doing writes, keep using the write backend even for reads // at least during the same request, to help against stale data. $this->_read = $this->_write; return $result; } /** * Check if a transaction has been started. * * @return boolean True if transaction has been started. */ public function transactionStarted() { $result = $this->_write->transactionStarted(); $this->_lastQuery = $this->_write->getLastQuery(); return $result; } /** * Begins the transaction (and turns off auto-committing). */ public function beginDbTransaction() { $result = $this->_write->beginDbTransaction(); $this->_lastQuery = $this->_write->getLastQuery(); return $result; } /** * Commits the transaction (and turns on auto-committing). */ public function commitDbTransaction() { $result = $this->_write->commitDbTransaction(); $this->_lastQuery = $this->_write->getLastQuery(); return $result; } /** * Rolls back the transaction (and turns on auto-committing). Must be * done if the transaction block raises an exception or returns false. */ public function rollbackDbTransaction() { $result = $this->_write->rollbackDbTransaction(); $this->_lastQuery = $this->_write->getLastQuery(); return $result; } /** * Appends +LIMIT+ and +OFFSET+ options to a SQL statement. * * @param string $sql SQL statement. * @param array $options TODO * * @return string */ public function addLimitOffset($sql, $options) { $result = $this->_read->addLimitOffset($sql, $options); $this->_lastQuery = $this->_write->getLastQuery(); return $result; } /** * Appends a locking clause to an SQL statement. * This method *modifies* the +sql+ parameter. * * # SELECT * FROM suppliers FOR UPDATE * add_lock! 'SELECT * FROM suppliers', :lock => true * add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE' * * @param string &$sql SQL statment. * @param array $options TODO. */ public function addLock(&$sql, array $options = array()) { $this->_write->addLock($sql, $options); $this->_lastQuery = $this->_write->getLastQuery(); } } Horde_Db-2.0.4/lib/Horde/Db/Migration/Base.php0000664000175000017500000001053012205351473016740 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Migration */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Migration */ class Horde_Db_Migration_Base { /** * The migration version * @var integer */ public $version = null; /** * The logger * @var Horde_Log_Logger */ protected $_logger; /** * Database connection adapter * @var Horde_Db_Adapter_Base */ protected $_connection; /*########################################################################## # Constructor ##########################################################################*/ /** */ public function __construct(Horde_Db_Adapter $connection, $version = null) { $this->_connection = $connection; $this->version = $version; } /*########################################################################## # Public ##########################################################################*/ /** * Proxy methods over to the connection * @param string $method * @param array $args */ public function __call($method, $args) { $a = array(); foreach ($args as $arg) { if (is_array($arg)) { $vals = array(); foreach ($arg as $key => $value) { $vals[] = var_export($key, true) . ' => ' . var_export($value, true); } $a[] = 'array(' . implode(', ', $vals) . ')'; } else { $a[] = var_export($arg, true); } } $this->say("$method(" . implode(", ", $a) . ")"); // benchmark method call $t = new Horde_Support_Timer(); $t->push(); $result = call_user_func_array(array($this->_connection, $method), $args); $time = $t->pop(); // print stats $this->say(sprintf("%.4fs", $time), 'subitem'); if (is_int($result)) { $this->say("$result rows", 'subitem'); } return $result; } public function upWithBechmarks() { $this->migrate('up'); } public function downWithBenchmarks() { $this->migrate('down'); } /** * Execute this migration in the named direction */ public function migrate($direction) { if (!method_exists($this, $direction)) { return; } if ($direction == 'up') { $this->announce("migrating"); } if ($direction == 'down') { $this->announce("reverting"); } $result = null; $t = new Horde_Support_Timer(); $t->push(); $result = $this->$direction(); $time = $t->pop(); if ($direction == 'up') { $this->announce("migrated (" . sprintf("%.4fs", $time) . ")"); $this->log(); } if ($direction == 'down') { $this->announce("reverted (" . sprintf("%.4fs", $time) . ")"); $this->log(); } return $result; } /** * @param string $text */ public function log($text = '') { if ($this->_logger) { $this->_logger->info($text); } } /** * @param Horde_Log_Logger $logger */ public function setLogger($logger) { $this->_logger = $logger; } /** * Announce migration * @param string $message */ public function announce($message) { $text = "$this->version " . get_class($this) . ": $message"; $length = 75 - strlen($text) > 0 ? 75 - strlen($text) : 0; $this->log(sprintf("== %s %s", $text, str_repeat('=', $length))); } /** * @param string $message * @param boolean $subitem */ public function say($message, $subitem = false) { $this->log(($subitem ? " ->" : "--") . " $message"); } } Horde_Db-2.0.4/lib/Horde/Db/Migration/Exception.php0000664000175000017500000000133612205351473020030 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Migration */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Migration */ class Horde_Db_Migration_Exception extends Horde_Db_Exception { } Horde_Db-2.0.4/lib/Horde/Db/Migration/Migrator.php0000664000175000017500000002301212205351473017651 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Migration */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Migration */ class Horde_Db_Migration_Migrator { /** * @var string */ protected $_direction = null; /** * @var string */ protected $_migrationsPath = null; /** * @var integer */ protected $_targetVersion = null; /** * @var string */ protected $_schemaTableName = 'schema_info'; /** * Constructor. * * @param Horde_Db_Adapter $connection A DB connection object. * @param Horde_Log_Logger $logger A logger object. * @param array $options Additional options for the migrator: * - migrationsPath: directory with the * migration files. * - schemaTableName: table for storing * the schema version. * * @throws Horde_Db_Migration_Exception */ public function __construct(Horde_Db_Adapter $connection, Horde_Log_Logger $logger = null, array $options = array()) { if (!$connection->supportsMigrations()) { throw new Horde_Db_Migration_Exception('This database does not yet support migrations'); } $this->_connection = $connection; $this->_logger = $logger ? $logger : new Horde_Support_Stub(); $this->_inflector = new Horde_Support_Inflector(); if (isset($options['migrationsPath'])) { $this->_migrationsPath = $options['migrationsPath']; } if (isset($options['schemaTableName'])) { $this->_schemaTableName = $options['schemaTableName']; } $this->_initializeSchemaInformation(); } /** * @param string $targetVersion */ public function migrate($targetVersion = null) { $currentVersion = $this->getCurrentVersion(); if ($targetVersion == null || $currentVersion < $targetVersion) { $this->up($targetVersion); } elseif ($currentVersion > $targetVersion) { // migrate down $this->down($targetVersion); } } /** * @param string $targetVersion */ public function up($targetVersion = null) { $this->_targetVersion = $targetVersion; $this->_direction = 'up'; $this->_doMigrate(); } /** * @param string $targetVersion */ public function down($targetVersion = null) { $this->_targetVersion = $targetVersion; $this->_direction = 'down'; $this->_doMigrate(); } /** * @return integer */ public function getCurrentVersion() { return in_array($this->_schemaTableName, $this->_connection->tables()) ? $this->_connection->selectValue('SELECT version FROM ' . $this->_schemaTableName) : 0; } /** * @return integer */ public function getTargetVersion() { $migrations = array(); foreach ($this->_getMigrationFiles() as $migrationFile) { list($version, $name) = $this->_getMigrationVersionAndName($migrationFile); $this->_assertUniqueMigrationVersion($migrations, $version); $migrations[$version] = $name; } // Sort by version. uksort($migrations, 'strnatcmp'); return key(array_reverse($migrations, true)); } /** * @param string $migrationsPath Path to migration files. */ public function setMigrationsPath($migrationsPath) { $this->_migrationsPath = $migrationsPath; } /** * @param Horde_Log_Logger $logger */ public function setLogger(Horde_Log_Logger $logger) { $this->_logger = $logger; } /** * @param Horde_Support_Inflector $inflector */ public function setInflector(Horde_Support_Inflector $inflector) { $this->_inflector = $inflector; } /** * Performs the migration. */ protected function _doMigrate() { foreach ($this->_getMigrationClasses() as $migration) { if ($this->_hasReachedTargetVersion($migration->version)) { $this->_logger->info('Reached target version: ' . $this->_targetVersion); return; } if ($this->_isIrrelevantMigration($migration->version)) { continue; } $this->_logger->info('Migrating ' . ($this->_direction == 'up' ? 'to ' : 'from ') . get_class($migration) . ' (' . $migration->version . ')'); $migration->migrate($this->_direction); $this->_setSchemaVersion($migration->version); } } /** * @return array */ protected function _getMigrationClasses() { $migrations = array(); foreach ($this->_getMigrationFiles() as $migrationFile) { require_once $migrationFile; list($version, $name) = $this->_getMigrationVersionAndName($migrationFile); $this->_assertUniqueMigrationVersion($migrations, $version); $migrations[$version] = $this->_getMigrationClass($name, $version); } // Sort by version. uksort($migrations, 'strnatcmp'); $sorted = array_values($migrations); return $this->_isDown() ? array_reverse($sorted) : $sorted; } /** * @param array $migrations * @param integer $version * * @throws Horde_Db_Migration_Exception */ protected function _assertUniqueMigrationVersion($migrations, $version) { if (isset($migrations[$version])) { throw new Horde_Db_Migration_Exception('Multiple migrations have the version number ' . $version); } } /** * Returns the list of migration files. * * @return array */ protected function _getMigrationFiles() { return array_keys( iterator_to_array( new RegexIterator( new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $this->_migrationsPath ) ), '/' . preg_quote(DIRECTORY_SEPARATOR, '/') . '\d+_.*\.php$/', RecursiveRegexIterator::MATCH, RegexIterator::USE_KEY ) ) ); } /** * Actually returns object, and not class. * * @param string $migrationName * @param integer $version * * @return Horde_Db_Migration_Base */ protected function _getMigrationClass($migrationName, $version) { $className = $this->_inflector->camelize($migrationName); $class = new $className($this->_connection, $version); $class->setLogger($this->_logger); return $class; } /** * @param string $migrationFile * * @return array ($version, $name) */ protected function _getMigrationVersionAndName($migrationFile) { preg_match_all('/([0-9]+)_([_a-z0-9]*).php/', $migrationFile, $matches); return array($matches[1][0], $matches[2][0]); } /** * @TODO */ protected function _initializeSchemaInformation() { if (in_array($this->_schemaTableName, $this->_connection->tables())) { return; } $schemaTable = $this->_connection->createTable($this->_schemaTableName, array('autoincrementKey' => false)); $schemaTable->column('version', 'integer'); $schemaTable->end(); $this->_connection->insert('INSERT INTO ' . $this->_schemaTableName . ' (version) VALUES (0)'); } /** * @param integer $version */ protected function _setSchemaVersion($version) { $version = $this->_isDown() ? $version - 1 : $version; if ($version) { $sql = 'UPDATE ' . $this->_schemaTableName . ' SET version = ' . (int)$version; $this->_connection->update($sql); } else { $this->_connection->dropTable($this->_schemaTableName); } } /** * @return boolean */ protected function _isUp() { return $this->_direction == 'up'; } /** * @return boolean */ protected function _isDown() { return $this->_direction == 'down'; } /** * @return boolean */ protected function _hasReachedTargetVersion($version) { if ($this->_targetVersion === null) { return false; } return ($this->_isUp() && $version - 1 >= $this->_targetVersion) || ($this->_isDown() && $version <= $this->_targetVersion); } /** * @param integer $version * * @return boolean */ protected function _isIrrelevantMigration($version) { return ($this->_isUp() && $version <= self::getCurrentVersion()) || ($this->_isDown() && $version > self::getCurrentVersion()); } } Horde_Db-2.0.4/lib/Horde/Db/Value/Binary.php0000664000175000017500000000171012205351473016435 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ /** * Encapsulation object for binary values to be used in SQL statements to ensure * proper quoting, escaping, retrieval, etc. * * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ class Horde_Db_Value_Binary implements Horde_Db_Value { /** * Binary value to be quoted * @var string */ protected $_value; /** * Constructor * * @param string $binaryValue */ public function __construct($binaryValue) { $this->_value = $binaryValue; } /** * @param Horde_Db_Adapter $db */ public function quote(Horde_Db_Adapter $db) { return $db->quoteBinary($this->_value); } } Horde_Db-2.0.4/lib/Horde/Db/Adapter.php0000664000175000017500000002310012205351473015512 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ interface Horde_Db_Adapter { /** * Returns the human-readable name of the adapter. Use mixed case - one * can always use downcase if needed. * * @return string */ public function adapterName(); /** * Does this adapter support migrations? Backend specific, as the * abstract adapter always returns +false+. * * @return boolean */ public function supportsMigrations(); /** * Does this adapter support using DISTINCT within COUNT? This is +true+ * for all adapters except sqlite. * * @return boolean */ public function supportsCountDistinct(); /** * Should primary key values be selected from their corresponding * sequence before the insert statement? If true, next_sequence_value * is called before each insert to set the record's primary key. * This is false for all adapters but Firebird. * * @return boolean */ public function prefetchPrimaryKey($tableName = null); /*########################################################################## # Connection Management ##########################################################################*/ /** * Connect to the db. */ public function connect(); /** * Is the connection active? * * @return boolean */ public function isActive(); /** * Reconnect to the db. */ public function reconnect(); /** * Disconnect from db. */ public function disconnect(); /** * Provides access to the underlying database connection. Useful for when * you need to call a proprietary method such as postgresql's * lo_* methods. * * @return resource */ public function rawConnection(); /*########################################################################## # Quoting ##########################################################################*/ /** * Quotes a string, escaping any special characters. * * @param string $string * @return string */ public function quoteString($string); /*########################################################################## # Database Statements ##########################################################################*/ /** * Returns an array of records with the column names as keys, and * column values as values. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return Iterator * @throws Horde_Db_Exception */ public function select($sql, $arg1 = null, $arg2 = null); /** * Returns an array of record hashes with the column names as keys and * column values as values. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectAll($sql, $arg1 = null, $arg2 = null); /** * Returns a record hash with the column names as keys and column values * as values. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectOne($sql, $arg1 = null, $arg2 = null); /** * Returns a single value from a record * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return string * @throws Horde_Db_Exception */ public function selectValue($sql, $arg1 = null, $arg2 = null); /** * Returns an array of the values of the first column in a select: * selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3] * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectValues($sql, $arg1 = null, $arg2 = null); /** * Returns an array where the keys are the first column of a select, and the * values are the second column: * * selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler'] * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return array * @throws Horde_Db_Exception */ public function selectAssoc($sql, $arg1 = null, $arg2 = null); /** * Executes the SQL statement in the context of this connection. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return Iterator * @throws Horde_Db_Exception */ public function execute($sql, $arg1 = null, $arg2 = null); /** * Returns the last auto-generated ID from the affected table. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a * query name. * @param string $arg2 If $arg1 contains bound parameters, the * query name. * @param string $pk TODO * @param integer $idValue TODO * @param string $sequenceName TODO * * @return integer Last inserted ID. * @throws Horde_Db_Exception */ public function insert($sql, $arg1 = null, $arg2 = null, $pk = null, $idValue = null, $sequenceName = null); /** * Executes the update statement and returns the number of rows affected. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return integer Number of rows affected. * @throws Horde_Db_Exception */ public function update($sql, $arg1 = null, $arg2 = null); /** * Executes the delete statement and returns the number of rows affected. * * @param string $sql SQL statement. * @param mixed $arg1 Either an array of bound parameters or a query * name. * @param string $arg2 If $arg1 contains bound parameters, the query * name. * * @return integer Number of rows affected. * @throws Horde_Db_Exception */ public function delete($sql, $arg1 = null, $arg2 = null); /** * Check if a transaction has been started. * * @return boolean True if transaction has been started. */ public function transactionStarted(); /** * Begins the transaction (and turns off auto-committing). */ public function beginDbTransaction(); /** * Commits the transaction (and turns on auto-committing). */ public function commitDbTransaction(); /** * Rolls back the transaction (and turns on auto-committing). Must be * done if the transaction block raises an exception or returns false. */ public function rollbackDbTransaction(); /** * Appends +LIMIT+ and +OFFSET+ options to a SQL statement. * * @param string $sql SQL statement. * @param array $options TODO * * @return string */ public function addLimitOffset($sql, $options); /** * Appends a locking clause to an SQL statement. * This method *modifies* the +sql+ parameter. * * # SELECT * FROM suppliers FOR UPDATE * add_lock! 'SELECT * FROM suppliers', :lock => true * add_lock! 'SELECT * FROM suppliers', :lock => ' FOR UPDATE' * * @param string &$sql SQL statment. * @param array $options TODO. */ public function addLock(&$sql, array $options = array()); } Horde_Db-2.0.4/lib/Horde/Db/Exception.php0000664000175000017500000000130112205351473016067 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ /** * Db Exception class. * * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ class Horde_Db_Exception extends Horde_Exception_Wrapped { } Horde_Db-2.0.4/lib/Horde/Db/SearchParser.php0000664000175000017500000001327112205351473016524 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ class Horde_Db_SearchParser { /** * Parses a keyword expression. * * @param string $column This is the SQL field name the resulting * expression should test against. * @param string $expr This is the keyword expression we want to parse. * * @return string The query expression. * @throws Horde_Db_Exception */ static public function parse($column, $expr) { /* First pass - scan the string for tokens. Bare words are tokens, or * the user can quote strings to have embedded spaces, keywords, or * parentheses. Parentheses can be used for grouping boolean * operators, and the boolean operators AND, OR, and NOT are all * recognized. * * The tokens are returned in the $tokens array -- an array of strings. * Each string in the array starts with either a `!' or a `='. `=' is * a bare word or quoted string we are searching for, and `!' indicates * a boolean operator or parenthesis. A token that starts with a '.' * indicates a PostgreSQL word boundary search. */ $tokens = array(); while (!empty($expr)) { $expr = preg_replace('/^\s+/', '', $expr); if (empty($expr)) { break; } if (substr($expr,0,1) == '(') { $expr = substr($expr, 1); $token = '!('; } elseif (substr($expr, 0, 1) == ')') { $expr = substr($expr, 1); $token = '!)'; } elseif (substr($expr, 0, 1) == ',') { $expr = substr($expr, 1); $token = '!OR'; } elseif (preg_match('/^(AND|OR|NOT)([^a-z].*)?$/i', $expr, $matches)) { $token = '!' . strtoupper($matches[1]); $expr = substr($expr, strlen($matches[1])); } elseif (preg_match('/^"(([^"]|\\[0-7]+|\\[Xx][0-9a-fA-F]+|\\[^Xx0-7])*)"/', $expr, $matches)) { $token = '=' . stripcslashes($matches[1]); $expr = substr($expr, strlen($matches[0])); } elseif (preg_match('/^[^\\s\\(\\),]+/', $expr, $matches)) { $token = '=' . $matches[0]; $expr = substr($expr,strlen($token)-1); } else { throw new Horde_Db_Exception('Syntax error in search terms'); } if ($token == '!AND') { /* !AND is implied by concatenation. */ continue; } $tokens[] = $token; } /* Call the expression parser. */ return self::_parseKeywords1($column, $tokens); } static protected function _parseKeywords1($column, &$tokens) { if (count($tokens) == 0) { throw new Horde_Db_Exception('Empty search terms'); } $lhs = self::_parseKeywords2($column, $tokens); if (count($tokens) == 0 || $tokens[0] != '!OR') { return $lhs; } array_shift($tokens); $rhs = self::_parseKeywords1($column, $tokens); return "($lhs OR $rhs)"; } static protected function _parseKeywords2($column, &$tokens) { $lhs = self::_parseKeywords3($column, $tokens); if (sizeof($tokens) == 0 || $tokens[0] == '!)' || $tokens[0] == '!OR') { return $lhs; } $rhs = self::_parseKeywords2($column, $tokens); return "($lhs AND $rhs)"; } static protected function _parseKeywords3($column, &$tokens) { if ($tokens[0] == '!NOT') { array_shift($tokens); $lhs = self::_parseKeywords4($column, $tokens); if (is_a($lhs, 'PEAR_Error')) { return $lhs; } return "(NOT $lhs)"; } return self::_parseKeywords4($column, $tokens); } static protected function _parseKeywords4($column, &$tokens) { if ($tokens[0] == '!(') { array_shift($tokens); $lhs = self::_parseKeywords1($column, $tokens); if (sizeof($tokens) == 0 || $tokens[0] != '!)') { throw new Horde_Db_Exception('Expected ")"'); } array_shift($tokens); return $lhs; } if (substr($tokens[0], 0, 1) != '=' && substr($tokens[0], 0, 2) != '=.') { throw new Horde_Db_Exception('Expected bare word or quoted search term'); } $val = strtolower(substr(array_shift($tokens), 1)); $val = addslashes(ereg_replace("([\\%])", "\\\\1", $val)); return "(LOWER($column) LIKE '%$val%')"; } } Horde_Db-2.0.4/lib/Horde/Db/StatementParser.php0000664000175000017500000000463512205351473017267 0ustar janjan * @author James Pepin * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ /** * Class for parsing a stream into individual SQL statements. * * @author Chuck Hagenbuch * @author James Pepin * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ class Horde_Db_StatementParser implements Iterator { protected $_count = 0; protected $_currentStatement; public function __construct($file) { if (is_string($file)) { $file = new SplFileObject($file, 'r'); } $this->_file = $file; } public function current() { if (is_null($this->_currentStatement)) { $this->rewind(); } return $this->_currentStatement; } public function key() { if (is_null($this->_currentStatement)) { $this->rewind(); } return $this->_count; } public function next() { if ($statement = $this->_getNextStatement()) { $this->_count++; return $statement; } return null; } public function rewind() { $this->_count = 0; $this->_currentStatement = null; $this->_file->rewind(); $this->next(); } public function valid() { return !$this->_file->eof() && $this->_file->isReadable(); } /** * Read the next sql statement from our file. Statements are terminated by * semicolons. * * @return string The next SQL statement in the file. */ protected function _getNextStatement() { $this->_currentStatement = ''; while (!$this->_file->eof()) { $line = $this->_file->fgets(); if (!trim($line)) { continue; } if (!$this->_currentStatement && substr($line, 0, 2) == '--') { continue; } $trimmedline = rtrim($line); if (substr($trimmedline, -1) == ';') { // Leave off the ending ; $this->_currentStatement .= substr($trimmedline, 0, -1); return $this->_currentStatement; } $this->_currentStatement .= $line; } return $this->_currentStatement; } } Horde_Db-2.0.4/lib/Horde/Db/Value.php0000664000175000017500000000103612205351473015212 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ /** * Interface for values with specific quoting rules. * * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ interface Horde_Db_Value { /** * @param Horde_Db_Adapter $db */ public function quote(Horde_Db_Adapter $db); } Horde_Db-2.0.4/lib/Horde/Db.php0000664000175000017500000000177012205351473014143 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ /** * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ class Horde_Db { /** * Specifies that the fetch method shall return each row as an array * indexed by column name as returned in the corresponding result set. */ const FETCH_ASSOC = 2; /** * Specifies that the fetch method shall return each row as an array * indexed by column number as returned in the corresponding result set, * starting at column 0. */ const FETCH_NUM = 3; /** * Specifies that the fetch method shall return each row as an array * indexed by both column name and number as returned in the corresponding * result set, starting at column 0. */ const FETCH_BOTH = 4; } Horde_Db-2.0.4/test/Horde/Db/Adapter/Mysql/ColumnDefinition.php0000664000175000017500000000756312205351473022315 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Mysql_ColumnDefinition extends Horde_Test_Case { public $conn; public function testConstruct() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'string' ); $this->assertEquals('col_name', $col->getName()); $this->assertEquals('string', $col->getType()); } public function testToSql() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'string' ); $this->assertEquals('`col_name` varchar(255)', $col->toSql()); } public function testToSqlLimit() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'string', 40 ); $this->assertEquals('`col_name` varchar(40)', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'string' ); $col->setLimit(40); $this->assertEquals('`col_name` varchar(40)', $col->toSql()); } public function testToSqlPrecisionScale() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'decimal', null, 5, 2 ); $this->assertEquals('`col_name` decimal(5, 2)', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'decimal' ); $col->setPrecision(5); $col->setScale(2); $this->assertEquals('`col_name` decimal(5, 2)', $col->toSql()); } public function testToSqlUnsigned() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'integer', null, null, null, true ); $this->assertEquals('`col_name` int(10) UNSIGNED', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'integer' ); $col->setUnsigned(true); $this->assertEquals('`col_name` int(10) UNSIGNED', $col->toSql()); } public function testToSqlNotNull() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'string', null, null, null, null, null, false ); $this->assertEquals('`col_name` varchar(255) NOT NULL', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'string' ); $col->setNull(false); $this->assertEquals('`col_name` varchar(255) NOT NULL', $col->toSql()); } public function testToSqlDefault() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'string', null, null, null, null, 'test' ); $this->assertEquals("`col_name` varchar(255) DEFAULT 'test'", $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'string' ); $col->setDefault('test'); $this->assertEquals("`col_name` varchar(255) DEFAULT 'test'", $col->toSql()); } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Mysql/ColumnTest.php0000664000175000017500000002264312205351473021140 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Mysql_ColumnTest extends PHPUnit_Framework_TestCase { /*########################################################################## # Construction ##########################################################################*/ public function testDefaultNull() { $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)'); $this->assertEquals(true, $col->isNull()); } public function testNotNull() { $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)', false); $this->assertEquals(false, $col->isNull()); } public function testName() { $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)'); $this->assertEquals('name', $col->getName()); } public function testSqlType() { $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)'); $this->assertEquals('varchar(255)', $col->getSqlType()); } public function testIsText() { $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'varchar(255)'); $this->assertTrue($col->isText()); $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'text'); $this->assertTrue($col->isText()); $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'int(11)'); $this->assertFalse($col->isText()); $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'float(11,1)'); $this->assertFalse($col->isText()); } public function testIsNumber() { $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'varchar(255)'); $this->assertFalse($col->isNumber()); $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'text'); $this->assertFalse($col->isNumber()); $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'int(11)'); $this->assertTrue($col->isNumber()); $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'float(11,1)'); $this->assertTrue($col->isNumber()); } /*########################################################################## # Types ##########################################################################*/ public function testTypeInteger() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)'); $this->assertEquals('integer', $col->getType()); $this->assertFalse($col->isUnsigned()); } public function testTypeIntegerUnsigned() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(10) UNSIGNED'); $this->assertTrue($col->isUnsigned()); } public function testTypeFloat() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'float(11,1)'); $this->assertEquals('float', $col->getType()); } public function testTypeDecimalPrecisionNone() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'decimal(11,0)'); $this->assertEquals('integer', $col->getType()); } public function testTypeDecimal() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'decimal(11,1)'); $this->assertEquals('decimal', $col->getType()); } public function testTypeDatetime() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'datetime'); $this->assertEquals('datetime', $col->getType()); } public function testTypeTimestamp() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'CURRENT_TIMESTAMP', 'timestamp'); $this->assertEquals('timestamp', $col->getType()); } public function testTypeTime() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'time'); $this->assertEquals('time', $col->getType()); } public function testTypeDate() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'date'); $this->assertEquals('date', $col->getType()); } public function testTypeText() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'text'); $this->assertEquals('text', $col->getType()); } public function testTypeBinary() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'blob(255)'); $this->assertEquals('binary', $col->getType()); } public function testTypeString() { $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)'); $this->assertEquals('string', $col->getType()); } /*########################################################################## # Extract Limit ##########################################################################*/ public function testExtractLimitInt() { $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'int(11)'); $this->assertEquals(11, $col->getLimit()); } public function testExtractLimitVarchar() { $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'varchar(255)'); $this->assertEquals(255, $col->getLimit()); } public function testExtractLimitDecimal() { $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'decimal(11,1)'); $this->assertEquals('11', $col->getLimit()); } public function testExtractLimitText() { $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'text'); $this->assertEquals(null, $col->getLimit()); } public function testExtractLimitNone() { $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL'); $this->assertEquals(null, $col->getLimit()); } /*########################################################################## # Extract Precision/Scale ##########################################################################*/ public function testExtractPrecisionScale() { $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'decimal(12,1)'); $this->assertEquals('12', $col->precision()); $this->assertEquals('1', $col->scale()); } /*########################################################################## # Type Cast Values ##########################################################################*/ public function testTypeCastInteger() { $col = new Horde_Db_Adapter_Mysql_Column('name', '1', 'int(11)', false); $this->assertEquals(1, $col->getDefault()); } public function testTypeCastFloat() { $col = new Horde_Db_Adapter_Mysql_Column('version', '1.0', 'float(11,1)', false); $this->assertEquals(1.0, $col->getDefault()); } public function testTypeCastString() { $col = new Horde_Db_Adapter_Mysql_Column('name', 'n/a', 'varchar(255)', false); $this->assertEquals('n/a', $col->getDefault()); } public function testTypeCastBooleanFalse() { $col = new Horde_Db_Adapter_Mysql_Column('is_active', '0', 'tinyint(1)', false); $this->assertSame(false, $col->getDefault()); } public function testTypeCastBooleanTrue() { $col = new Horde_Db_Adapter_Mysql_Column('is_active', '1', 'tinyint(1)', false); $this->assertSame(true, $col->getDefault()); } /*########################################################################## # Column Types ##########################################################################*/ public function testColumnTypeEnum() { $col = new Horde_Db_Adapter_Mysql_Column('user', 'NULL', "enum('derek', 'mike')"); $this->assertEquals('string', $col->getType()); } public function testColumnTypeBoolean() { $col = new Horde_Db_Adapter_Mysql_Column('is_active', 'NULL', 'tinyint(1)'); $this->assertEquals('boolean', $col->getType()); } /*########################################################################## # Defaults ##########################################################################*/ public function testDefaultDatetime() { $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'datetime'); $this->assertEquals(null, $col->getDefault()); } public function testDefaultInteger() { $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'int(11)'); $this->assertEquals(null, $col->getDefault()); } public function testDefaultString() { $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'varchar(255)'); $this->assertEquals('', $col->getDefault()); } public function testDefaultText() { $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'text'); $this->assertEquals('', $col->getDefault()); } public function testDefaultBinary() { $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'blob(255)'); $this->assertEquals('', $col->getDefault()); } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Mysql/TableDefinition.php0000664000175000017500000000314212205351473022074 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Mysql_TableDefinition extends Horde_Test_Case { public $conn; /*########################################################################## # Public methods ##########################################################################*/ public function testConstruct() { } public function testName() { } public function testGetOptions() { } public function testPrimaryKey() { } public function testColumn() { } public function testToSql() { } /*########################################################################## # Array Access ##########################################################################*/ public function testOffsetExists() { } public function testOffsetGet() { } public function testOffsetSet() { } public function testOffsetUnset() { } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Pdo/MysqlTest.php0000664000175000017500000016162312205351473020427 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Pdo_MysqlTest extends Horde_Test_Case { protected static $columnTest; protected static $tableTest; protected static $skip = true; protected static $reason = 'The PDO_MySQL adapter is not available'; public static function setUpBeforeClass() { if (extension_loaded('pdo') && in_array('mysql', PDO::getAvailableDrivers())) { self::$skip = false; list($conn,) = self::getConnection(); if (self::$skip) { return; } $conn->disconnect(); } require_once __DIR__ . '/../Mysql/ColumnDefinition.php'; require_once __DIR__ . '/../Mysql/TableDefinition.php'; self::$columnTest = new Horde_Db_Adapter_Mysql_ColumnDefinition(); self::$tableTest = new Horde_Db_Adapter_Mysql_TableDefinition(); } public static function getConnection($overrides = array()) { $config = Horde_Test_Case::getConfig('DB_ADAPTER_PDO_MYSQL_TEST_CONFIG', null, array('host' => 'localhost', 'username' => '', 'password' => '', 'dbname' => 'test')); if (isset($config['db']['adapter']['pdo']['mysql']['test'])) { $config = $config['db']['adapter']['pdo']['mysql']['test']; } if (!is_array($config)) { self::$skip = true; self::$reason = 'No configuration for pdo_mysql test'; return; } $config = array_merge($config, $overrides); $conn = new Horde_Db_Adapter_Pdo_Mysql($config); $cache = new Horde_Cache(new Horde_Cache_Storage_Mock()); $conn->setCache($cache); return array($conn, $cache); } protected function setUp() { if (self::$skip) { $this->markTestSkipped(self::$reason); } list($this->_conn, $this->_cache) = self::getConnection(); self::$columnTest->conn = $this->_conn; self::$tableTest->conn = $this->_conn; // clear out detritus from any previous test runs. $this->_dropTestTables(); $table = $this->_conn->createTable('unit_tests'); $table->column('integer_value', 'integer', array('limit' => 11, 'default' => 0)); $table->column('string_value', 'string', array('limit' => 255, 'default' => '')); $table->column('text_value', 'text', array('null' => false, 'default' => '')); $table->column('float_value', 'float', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('decimal_value', 'decimal', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('datetime_value', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('date_value', 'date', array('default' => '0000-00-00')); $table->column('time_value', 'time', array('default' => '00:00:00')); $table->column('blob_value', 'binary', array('null' => false, 'default' => '')); $table->column('boolean_value', 'boolean', array('default' => false)); $table->column('email_value', 'string', array('limit' => 255, 'default' => '')); $table->end(); $this->_conn->addIndex('unit_tests', 'string_value', array('name' => 'string_value')); $this->_conn->addIndex('unit_tests', 'integer_value', array('name' => 'integer_value', 'unique' => true)); $this->_conn->addIndex('unit_tests', array('integer_value', 'string_value'), array('name' => 'integer_string')); // read sql file for statements $statements = array(); $current_stmt = ''; $fp = fopen(__DIR__ . '/../../fixtures/unit_tests.sql', 'r'); while ($line = fgets($fp, 8192)) { $line = rtrim(preg_replace('/^(.*)--.*$/s', '\1', $line)); if (!$line) { continue; } $current_stmt .= $line; if (substr($line, -1) == ';') { // leave off the ending ; $statements[] = substr($current_stmt, 0, -1); $current_stmt = ''; } } // run statements foreach ($statements as $stmt) { $this->_conn->execute($stmt); } } protected function tearDown() { // clean up $this->_dropTestTables(); // close connection $this->_conn->disconnect(); } /*########################################################################## # Connection ##########################################################################*/ public function testConnect() { $this->assertTrue($this->_conn->isActive()); } public function testDisconnect() { $this->_conn->disconnect(); $this->assertFalse($this->_conn->isActive()); $this->_conn->connect(); $this->assertTrue($this->_conn->isActive()); } public function testReconnect() { $this->_conn->reconnect(); $this->assertTrue($this->_conn->isActive()); } /*########################################################################## # Accessor ##########################################################################*/ public function testAdapterName() { $this->assertEquals('PDO_MySQL', $this->_conn->adapterName()); } public function testSupportsMigrations() { $this->assertTrue($this->_conn->supportsMigrations()); } public function testSupportsCountDistinct() { $this->assertTrue($this->_conn->supportsCountDistinct()); } public function testGetCharset() { $this->assertEquals('utf8', strtolower($this->_conn->getCharset())); } /*########################################################################## # Database Statements ##########################################################################*/ public function testExecute() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->execute($sql); $row = $result->fetch(); $this->assertEquals(1, $row['id']); } public function testSelect() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->select($sql); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParameters() { $sql = "SELECT * FROM unit_tests WHERE id=?"; $result = $this->_conn->select($sql, array(1)); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParametersQuotesString() { $sql = "SELECT * FROM unit_tests WHERE string_value=?"; $result = $this->_conn->select($sql, array('name a')); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectAll() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectAll($sql); $this->assertInternalType('array', $result); $this->assertGreaterThan(0, count($result)); $this->assertEquals(1, $result[0]['id']); } public function testSelectOne() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectOne($sql); $this->assertEquals(1, $result['id']); } public function testSelectValue() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectValue($sql); $this->assertEquals(1, $result); } public function testSelectValues() { $sql = "SELECT * FROM unit_tests"; $result = $this->_conn->selectValues($sql); $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result); } public function testInsert() { $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $result = $this->_conn->insert($sql); $this->assertEquals(7, $result); } public function testUpdate() { $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)"; $result = $this->_conn->update($sql); $this->assertEquals(1, $result); } public function testDelete() { $sql = "DELETE FROM unit_tests WHERE id IN (1,2)"; $result = $this->_conn->delete($sql); $this->assertEquals(2, $result); } public function testTransactionStarted() { $this->assertFalse($this->_conn->transactionStarted()); $this->_conn->beginDbTransaction(); $this->assertTrue($this->_conn->transactionStarted()); $this->_conn->commitDbTransaction(); $this->assertFalse($this->_conn->transactionStarted()); } public function testTransactionCommit() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql); $this->_conn->commitDbTransaction(); // make sure it inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals('999', $this->_conn->selectValue($sql)); } public function testTransactionRollback() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql); $this->_conn->rollbackDbTransaction(); // make sure it inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals(null, $this->_conn->selectValue($sql)); } /*########################################################################## # Quoting ##########################################################################*/ public function testQuoteNull() { $this->assertEquals('NULL', $this->_conn->quote(null)); } public function testQuoteTrue() { $this->assertEquals('1', $this->_conn->quote(true)); } public function testQuoteFalse() { $this->assertEquals('0', $this->_conn->quote(false)); } public function testQuoteString() { $this->assertEquals("'my string'", $this->_conn->quote('my string')); } public function testQuoteDirtyString() { $this->assertEquals("'derek\'s string'", $this->_conn->quote('derek\'s string')); } public function testQuoteColumnName() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)'); $this->assertEquals('1', $this->_conn->quote(true, $col)); } public function testQuoteBinary() { // Test string is foo\0ba'r - should be 8 bytes long $original = base64_decode('Zm9vAGJhJ3I='); $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); $this->_conn->insert('INSERT INTO binary_testings (data) VALUES (?)', array(new Horde_Db_Value_Binary($original))); $retrieved = $this->_conn->selectValue('SELECT data FROM binary_testings'); $columns = $this->_conn->columns('binary_testings'); $retrieved = $columns['data']->binaryToString($retrieved); $this->assertEquals($original, $retrieved); } /*########################################################################## # Schema Statements ##########################################################################*/ /** * We specifically do a manual INSERT here, and then test only the SELECT * functionality. This allows us to more easily catch INSERT being broken, * but SELECT actually working fine. */ public function testNativeDecimalInsertManualVsAutomatic() { $table = $this->_conn->createTable('users'); $table->column('company_id', 'integer', array('limit' => 11)); $table->column('name', 'string', array('limit' => 255, 'default' => '')); $table->column('first_name', 'string', array('limit' => 40, 'default' => '')); $table->column('approved', 'boolean', array('default' => true)); $table->column('type', 'string', array('limit' => 255, 'default' => '')); $table->column('created_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('created_on', 'date', array('default' => '0000-00-00')); $table->column('updated_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('updated_on', 'date', array('default' => '0000-00-00')); $table->end(); $correctValue = 12345678901234567890.0123456789; $this->_conn->addColumn("users", "wealth", 'decimal', array('precision' => 30, 'scale' => 10)); // do a manual insertion $this->_conn->execute("INSERT INTO users (wealth) VALUES ('12345678901234567890.0123456789')"); // SELECT @todo - type cast attribute values $user = (object)$this->_conn->selectOne('SELECT * FROM users'); // assert_kind_of BigDecimal, row.wealth // If this assert fails, that means the SELECT is broken! $this->assertEquals($correctValue, $user->wealth); // Reset to old state $this->_conn->delete('DELETE FROM users'); // Now use the Adapter insertion $this->_conn->insert('INSERT INTO users (wealth) VALUES (12345678901234567890.0123456789)'); // SELECT @todo - type cast attribute values $user = (object)$this->_conn->selectOne('SELECT * FROM users'); // assert_kind_of BigDecimal, row.wealth // If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken! $this->assertEquals($correctValue, $user->wealth); } public function testNativeTypes() { $table = $this->_conn->createTable('users'); $table->column('company_id', 'integer', array('limit' => 11)); $table->column('name', 'string', array('limit' => 255, 'default' => '')); $table->column('first_name', 'string', array('limit' => 40, 'default' => '')); $table->column('approved', 'boolean', array('default' => true)); $table->column('type', 'string', array('limit' => 255, 'default' => '')); $table->column('created_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('created_on', 'date', array('default' => '0000-00-00')); $table->column('updated_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('updated_on', 'date', array('default' => '0000-00-00')); $table->end(); $this->_conn->addColumn("users", "last_name", 'string'); $this->_conn->addColumn("users", "bio", 'text'); $this->_conn->addColumn("users", "age", 'integer'); $this->_conn->addColumn("users", "height", 'float'); $this->_conn->addColumn("users", "wealth", 'decimal', array('precision' => '30', 'scale' => '10')); $this->_conn->addColumn("users", "birthday", 'datetime'); $this->_conn->addColumn("users", "favorite_day", 'date'); $this->_conn->addColumn("users", "moment_of_truth", 'datetime'); $this->_conn->addColumn("users", "male", 'boolean'); $this->_conn->insert('INSERT INTO users (first_name, last_name, bio, age, height, wealth, birthday, favorite_day, moment_of_truth, male, company_id) ' . "VALUES ('bob', 'bobsen', 'I was born ....', 18, 1.78, 12345678901234567890.0123456789, '2005-01-01 12:23:40', '1980-03-05', '1582-10-10 21:40:18', 1, 1)"); $bob = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('bob', $bob->first_name); $this->assertEquals('bobsen', $bob->last_name); $this->assertEquals('I was born ....', $bob->bio); $this->assertEquals(18, $bob->age); // Test for 30 significent digits (beyond the 16 of float), 10 of them // after the decimal place. $this->assertEquals('12345678901234567890.0123456789', $bob->wealth); $this->assertEquals(1, $bob->male); // @todo - type casting } public function testNativeDatabaseTypes() { $types = $this->_conn->nativeDatabaseTypes(); $this->assertEquals(array('name' => 'int', 'limit' => 11), $types['integer']); } public function testUnabstractedDatabaseDependentTypes() { $this->_createTestUsersTable(); $this->_conn->delete('DELETE FROM users'); $this->_conn->addColumn('users', 'intelligence_quotient', 'tinyint'); $this->_conn->insert('INSERT INTO users (intelligence_quotient) VALUES (300)'); $jonnyg = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('127', $jonnyg->intelligence_quotient); } public function testTableAliasLength() { $len = $this->_conn->tableAliasLength(); $this->assertEquals(255, $len); } public function testTableAliasFor() { $alias = $this->_conn->tableAliasFor('my_table_name'); $this->assertEquals('my_table_name', $alias); } public function testTables() { $tables = $this->_conn->tables(); $this->assertTrue(count($tables) > 0); $this->assertContains('unit_tests', $tables); } public function testPrimaryKey() { $pk = $this->_conn->primaryKey('unit_tests'); $this->assertEquals('id', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->assertEquals('id', $pk->columns[0]); $table = $this->_conn->createTable('pk_tests', array('autoincrementKey' => false)); $table->column('foo', 'string'); $table->column('bar', 'string'); $table->end(); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', 'foo'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->_conn->removePrimaryKey('pk_tests'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', array('foo', 'bar')); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo,bar', (string)$pk); } public function testIndexes() { $indexes = $this->_conn->indexes('unit_tests'); $this->assertEquals(3, count($indexes)); // unique index $col = array('integer_value'); $this->assertEquals('unit_tests', $indexes[0]->table); $this->assertEquals('integer_value', $indexes[0]->name); $this->assertEquals(true, $indexes[0]->unique); $this->assertEquals($col, $indexes[0]->columns); // normal index $col = array('string_value'); $this->assertEquals('unit_tests', $indexes[1]->table); $this->assertEquals('string_value', $indexes[1]->name); $this->assertEquals(false, $indexes[1]->unique); $this->assertEquals($col, $indexes[1]->columns); // multi-column index $col = array('integer_value', 'string_value'); $this->assertEquals('unit_tests', $indexes[2]->table); $this->assertEquals('integer_string', $indexes[2]->name); $this->assertEquals(false, $indexes[2]->unique); $this->assertEquals($col, $indexes[2]->columns); } public function testColumns() { $columns = $this->_conn->columns('unit_tests'); $this->assertEquals(12, count($columns)); $col = $columns['id']; $this->assertEquals('id', $col->getName()); $this->assertEquals('integer', $col->getType()); $this->assertEquals(false, $col->isNull()); $this->assertEquals(10, $col->getLimit()); $this->assertEquals(true, $col->isUnsigned()); $this->assertEquals('', $col->getDefault()); $this->assertEquals('int(10) unsigned', $col->getSqlType()); $this->assertEquals(false, $col->isText()); $this->assertEquals(true, $col->isNumber()); } public function testCreateTable() { $this->_createTestTable('sports'); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableNoPk() { $this->_createTestTable('sports', array('autoincrementKey' => false)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertNull($this->_conn->selectValue($sql)); } catch (Exception $e) { return; } $this->fail("Expected exception for no pk"); } public function testCreateTableWithNamedPk() { $this->_createTestTable('sports', array('autoincrementKey' => 'sports_id')); $sql = "SELECT sports_id FROM sports WHERE sports_id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertNull($this->_conn->selectValue($sql)); } catch (Exception $e) { return; } $this->fail("Expected exception for wrong pk name"); } public function testCreateTableWithSeparatePk() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'autoincrementKey'); $pkColumn = $table['foo']; $this->assertEquals('`foo` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', $pkColumn->toSql()); } public function testCreateTableCompositePk() { $table = $this->_conn->createTable('testings', array('autoincrementKey' => array('a_id', 'b_id'))); $table->column('a_id', 'integer'); $table->column('b_id', 'integer'); $table->end(); $pk = $this->_conn->primaryKey('testings'); $this->assertEquals(array('a_id', 'b_id'), $pk->columns); } public function testCreateTableForce() { $this->_createTestTable('sports'); $this->_createTestTable('sports', array('force' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableTemporary() { $this->_createTestTable('sports', array('temporary' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableAddsId() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[] = $col->getName(); } sort($columns); $this->assertEquals(array('foo', 'id'), $columns); } public function testCreateTableWithNotNullColumn() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('null' => false)); $table->end(); try { $this->_conn->execute("INSERT INTO testings (foo) VALUES (NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testCreateTableWithDefaults() { $table = $this->_conn->createTable('testings'); $table->column('one', 'string', array('default' => 'hello')); $table->column('two', 'boolean', array('default' => true)); $table->column('three', 'boolean', array('default' => false)); $table->column('four', 'integer', array('default' => 1)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals('hello', $columns['one']->getDefault()); $this->assertTrue($columns['two']->getDefault()); $this->assertFalse($columns['three']->getDefault()); $this->assertEquals(1, $columns['four']->getDefault()); } public function testCreateTableWithLimits() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('limit' => 80)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals(80, $columns['foo']->getLimit()); } public function testCreateTableWithBinaryColumn() { try { $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); } catch (Exception $e) { $this->fail('Unexepected exception raised'); } $columns = $this->_conn->columns('binary_testings'); foreach ($columns as $c) { if ($c->getName() == 'data') { $dataColumn = $c; } } $this->assertEquals('', $dataColumn->getDefault()); } public function testRenameTable() { // Simple rename then select test $this->_createTestTable('sports'); $this->_conn->renameTable('sports', 'my_sports'); $sql = "SELECT id FROM my_sports WHERE id = 1"; $this->assertEquals("1", $this->_conn->selectValue($sql)); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); // Rename then insert test $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->execute($sql); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM octopuses WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testRenameTableWithAnIndex() { $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->addIndex('octopuses', 'url'); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->execute($sql); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); $indexes = $this->_conn->indexes('octopi'); $this->assertEquals('url', $indexes[0]->columns[0]); } public function testDropTable() { $this->_createTestTable('sports'); $this->_conn->dropTable('sports'); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testAddColumn() { $this->_createTestTable('sports'); $this->_conn->addColumn('sports', 'modified_at', 'date'); $this->_conn->update("UPDATE sports SET modified_at = '2007-01-01'"); $sql = "SELECT modified_at FROM sports WHERE id = 1"; $this->assertEquals("2007-01-01", $this->_conn->selectValue($sql)); $this->_conn->addColumn('sports', 'with_default', 'integer', array('default' => 1)); $sql = "SELECT with_default FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testRemoveColumn() { $this->_createTestTable('sports'); $sql = "SELECT name FROM sports WHERE id = 1"; $this->assertEquals("mlb", $this->_conn->selectValue($sql)); $this->_conn->removeColumn('sports', 'name'); try { $sql = "SELECT name FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Column exists where it shouldn't have"); } public function testChangeColumn() { $this->_createTestUsersTable(); $this->_conn->addColumn('users', 'age', 'integer'); $oldColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertTrue($found); $this->_conn->changeColumn('users', 'age', 'string'); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'string') { $found = true; } } $this->assertTrue($found); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => false)); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == false) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => true)); } public function testChangeColumnDefault() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'name'); $this->assertEquals('', $beforeChange->getDefault()); $this->_conn->changeColumnDefault('sports', 'name', 'test'); $afterChange = $this->_getColumn('sports', 'name'); $this->assertEquals('test', $afterChange->getDefault()); } public function testChangeColumnType() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string'); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar(255)', $afterChange->getSqlType()); $table = $this->_conn->createTable('text_to_binary'); $table->column('data', 'text'); $table->end(); $this->_conn->insert('INSERT INTO text_to_binary (data) VALUES (?)', array("foo\0bar")); $this->_conn->changeColumn('text_to_binary', 'data', 'binary'); $afterChange = $this->_getColumn('text_to_binary', 'data'); $this->assertEquals('longblob', $afterChange->getSqlType()); $this->assertEquals( "foo\0bar", $this->_conn->selectValue('SELECT data FROM text_to_binary')); } public function testChangeColumnLimit() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string', array('limit' => '40')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar(40)', $afterChange->getSqlType()); } public function testChangeColumnPrecisionScale() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'decimal', array('precision' => '5', 'scale' => '2')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('decimal(5,2)', $afterChange->getSqlType()); } public function testChangeColumnUnsigned() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'integer'); $table->end(); $beforeChange = $this->_getColumn('testings', 'foo'); $this->assertFalse($beforeChange->isUnsigned()); $this->_conn->execute("INSERT INTO testings (id, foo) VALUES (1, -1)"); $this->_conn->changeColumn('testings', 'foo', 'integer', array('unsigned' => true)); $afterChange = $this->_getColumn('testings', 'foo'); $this->assertTrue($afterChange->isUnsigned()); $row = (object)$this->_conn->selectOne('SELECT * FROM testings'); $this->assertEquals(0, $row->foo); } public function testRenameColumn() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'nick_name'); $this->assertTrue(in_array('nick_name', $this->_columnNames('users'))); $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->renameColumn('sports', 'is_college', 'is_renamed'); $afterChange = $this->_getColumn('sports', 'is_renamed'); $this->assertEquals('tinyint(1)', $afterChange->getSqlType()); } public function testRenameColumnWithSqlReservedWord() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'group'); $this->assertTrue(in_array('group', $this->_columnNames('users'))); } public function testAddIndex() { $this->_createTestUsersTable(); // Limit size of last_name and key columns to support Firebird index limitations $this->_conn->addColumn('users', 'last_name', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'key', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'administrator', 'boolean'); $this->_conn->addIndex('users', 'last_name'); $this->_conn->removeIndex('users', 'last_name'); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('column' => array('last_name', 'first_name'))); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('name' => 'index_users_on_last_name_and_first_name')); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', 'last_name_and_first_name'); // quoting $this->_conn->addIndex('users', array('key'), array('name' => 'key_idx', 'unique' => true)); $this->_conn->removeIndex('users', array('name' => 'key_idx', 'unique' => true)); $this->_conn->addIndex('users', array('last_name', 'first_name', 'administrator'), array('name' => "named_admin")); $this->_conn->removeIndex('users', array('name' => 'named_admin')); } public function testAddIndexDefault() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); } public function testAddIndexMultiColumn() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); } public function testAddIndexUnique() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('unique' => true)); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertTrue($index->unique); } public function testAddIndexName() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertEquals('test', $index->name); } public function testRemoveIndexSingleColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => 'is_college')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testRemoveIndexMultiColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => array('name', 'is_college'))); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); } public function testRemoveIndexByName() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testIndexNameInvalid() { try { $name = $this->_conn->indexName('sports'); } catch (Horde_Db_Exception $e) { return; } $this->fail("Adding an index with crappy options worked where it shouldn't have"); } public function testIndexNameBySingleColumn() { $name = $this->_conn->indexName('sports', array('column' => 'is_college')); $this->assertEquals('index_sports_on_is_college', $name); } public function testIndexNameByMultiColumn() { $name = $this->_conn->indexName('sports', array('column' => array('name', 'is_college'))); $this->assertEquals('index_sports_on_name_and_is_college', $name); } public function testIndexNameByName() { $name = $this->_conn->indexName('sports', array('name' => 'test')); $this->assertEquals('test', $name); } public function testTypeToSqlTypePrimaryKey() { $result = $this->_conn->typeToSql('autoincrementKey'); $this->assertEquals('int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', $result); } public function testTypeToSqlTypeString() { $result = $this->_conn->typeToSql('string'); $this->assertEquals('varchar(255)', $result); } public function testTypeToSqlTypeText() { $result = $this->_conn->typeToSql('text'); $this->assertEquals('text', $result); } public function testTypeToSqlTypeBinary() { $result = $this->_conn->typeToSql('binary'); $this->assertEquals('longblob', $result); } public function testTypeToSqlTypeFloat() { $result = $this->_conn->typeToSql('float'); $this->assertEquals('float', $result); } public function testTypeToSqlTypeDatetime() { $result = $this->_conn->typeToSql('datetime'); $this->assertEquals('datetime', $result); } public function testTypeToSqlTypeTimestamp() { $result = $this->_conn->typeToSql('timestamp'); $this->assertEquals('datetime', $result); } public function testTypeToSqlInt() { $result = $this->_conn->typeToSql('integer'); $this->assertEquals('int(11)', $result); } public function testTypeToSqlIntUnsigned() { $result = $this->_conn->typeToSql('integer', null, null, null, true); $this->assertEquals('int(10) UNSIGNED', $result); } public function testTypeToSqlIntLimit() { $result = $this->_conn->typeToSql('integer', '1'); $this->assertEquals('int(1)', $result); } public function testTypeToSqlDecimalPrecision() { $result = $this->_conn->typeToSql('decimal', null, '5'); $this->assertEquals('decimal(5)', $result); } public function testTypeToSqlDecimalScale() { $result = $this->_conn->typeToSql('decimal', null, '5', '2'); $this->assertEquals('decimal(5, 2)', $result); } public function testTypeToSqlBoolean() { $result = $this->_conn->typeToSql('boolean'); $this->assertEquals('tinyint(1)', $result); } public function testAddColumnOptions() { $result = $this->_conn->addColumnOptions("test", array()); $this->assertEquals("test", $result); } public function testAddColumnOptionsDefault() { $options = array('default' => '0'); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test DEFAULT '0'", $result); } public function testAddColumnOptionsNull() { $options = array('null' => true); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test", $result); } public function testAddColumnOptionsNotNull() { $options = array('null' => false); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test NOT NULL", $result); } public function testAddColumnNotNullWithoutDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => '')); try { $this->_conn->execute("INSERT INTO testings (foo, bar) VALUES ('hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddColumnNotNullWithDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->execute("INSERT INTO testings (id, foo) VALUES ('1', 'hello')"); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => 'default')); try { $this->_conn->execute("INSERT INTO testings (id, foo, bar) VALUES (2, 'hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddRemoveSingleField() { $this->_createTestUsersTable(); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); $this->_conn->addColumn('users', 'last_name', 'string'); $this->assertTrue(in_array('last_name', $this->_columnNames('users'))); $this->_conn->removeColumn('users', 'last_name'); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); } public function testAddRename() { $this->_createTestUsersTable(); $this->_conn->delete('DELETE FROM users'); $this->_conn->addColumn('users', 'girlfriend', 'string'); $this->_conn->insert("INSERT INTO users (girlfriend) VALUES ('bobette')"); $this->_conn->renameColumn('users', 'girlfriend', 'exgirlfriend'); $bob = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('bobette', $bob->exgirlfriend); } public function testDistinct() { $result = $this->_conn->distinct("test"); $this->assertEquals("DISTINCT test", $result); } public function testAddOrderByForAssocLimiting() { $result = $this->_conn->addOrderByForAssocLimiting("SELECT * FROM documents ", array('order' => 'name DESC')); $this->assertEquals("SELECT * FROM documents ORDER BY name DESC", $result); } public function testInterval() { $this->assertEquals('INTERVAL 1 DAY', $this->_conn->interval('1 DAY', '')); } public function testModifyDate() { $modifiedDate = $this->_conn->modifyDate('start', '+', 1, 'DAY'); $this->assertEquals('start + INTERVAL \'1\' DAY', $modifiedDate); $t = $this->_conn->createTable('dates'); $t->column('start', 'datetime'); $t->column('end', 'datetime'); $t->end(); $this->_conn->insert( 'INSERT INTO dates (start, end) VALUES (?, ?)', array( '2011-12-10 00:00:00', '2011-12-11 00:00:00' ) ); $this->assertEquals( 1, $this->_conn->selectValue('SELECT COUNT(*) FROM dates WHERE ' . $modifiedDate . ' = end') ); } public function testBuildClause() { $this->assertEquals( 'bitmap & 2', $this->_conn->buildClause('bitmap', '&', 2)); $this->assertEquals( array('bitmap & ?', array(2)), $this->_conn->buildClause('bitmap', '&', 2, true)); $this->assertEquals( 'bitmap | 2', $this->_conn->buildClause('bitmap', '|', 2)); $this->assertEquals( array('bitmap | ?', array(2)), $this->_conn->buildClause('bitmap', '|', 2, true)); $this->assertEquals( "LOWER(name) LIKE LOWER('%search%')", $this->_conn->buildClause('name', 'LIKE', "search")); $this->assertEquals( array("LOWER(name) LIKE LOWER(?)", array('%search%')), $this->_conn->buildClause('name', 'LIKE', "search", true)); $this->assertEquals( "LOWER(name) LIKE LOWER('%search\&replace\?%')", $this->_conn->buildClause('name', 'LIKE', "search&replace?")); $this->assertEquals( array("LOWER(name) LIKE LOWER(?)", array('%search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true)); $this->assertEquals( "(LOWER(name) LIKE LOWER('search\&replace\?%') OR LOWER(name) LIKE LOWER('% search\&replace\?%'))", $this->_conn->buildClause('name', 'LIKE', "search&replace?", false, array('begin' => true))); $this->assertEquals( array("(LOWER(name) LIKE LOWER(?) OR LOWER(name) LIKE LOWER(?))", array('search&replace?%', '% search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true, array('begin' => true))); $this->assertEquals( 'value = 2', $this->_conn->buildClause('value', '=', 2)); $this->assertEquals( array('value = ?', array(2)), $this->_conn->buildClause('value', '=', 2, true)); $this->assertEquals( "value = 'foo'", $this->_conn->buildClause('value', '=', 'foo')); $this->assertEquals( array('value = ?', array('foo')), $this->_conn->buildClause('value', '=', 'foo', true)); $this->assertEquals( "value = 'foo\?bar'", $this->_conn->buildClause('value', '=', 'foo?bar')); $this->assertEquals( array('value = ?', array('foo?bar')), $this->_conn->buildClause('value', '=', 'foo?bar', true)); } public function testInsertAndReadInCp1257() { list($conn,) = self::getConnection(array('charset' => 'cp1257')); $table = $conn->createTable('charset_cp1257'); $table->column('text', 'string'); $table->end(); $input = file_get_contents(__DIR__ . '/../../fixtures/charsets/cp1257.txt'); $conn->insert("INSERT INTO charset_cp1257 (text) VALUES (?)", array($input)); $output = $conn->selectValue('SELECT text FROM charset_cp1257'); $this->assertEquals($input, $output); } public function testInsertAndReadInUtf8() { list($conn,) = self::getConnection(array('charset' => 'utf8')); $table = $conn->createTable('charset_utf8'); $table->column('text', 'string'); $table->end(); $input = file_get_contents(__DIR__ . '/../../fixtures/charsets/utf8.txt'); $conn->insert("INSERT INTO charset_utf8 (text) VALUES (?)", array($input)); $output = $conn->selectValue('SELECT text FROM charset_utf8'); $this->assertEquals($input, $output); } /*########################################################################## # Table cache ##########################################################################*/ public function testCachedTableIndexes() { // remove any current cache. $this->_cache->set('tables/indexes/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/indexes/cache_table', 0)); $this->_createTestTable('cache_table'); $idxs = $this->_conn->indexes('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/indexes/cache_table', 0)); } public function testCachedTableColumns() { // remove any current cache. $this->_cache->set('tables/columns/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/columns/cache_table', 0)); $this->_createTestTable('cache_table'); $cols = $this->_conn->columns('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/columns/cache_table', 0)); } /*########################################################################## # Protected ##########################################################################*/ /** * Create table to perform tests on */ protected function _createTestTable($name, $options=array()) { $table = $this->_conn->createTable($name, $options); $table->column('name', 'string'); $table->column('is_college', 'boolean'); $table->end(); try { // make sure table was created $sql = "INSERT INTO $name VALUES (1, 'mlb', 0)"; $this->_conn->insert($sql); } catch (Exception $e) {} } protected function _createTestUsersTable() { $table = $this->_conn->createTable('users'); $table->column('company_id', 'integer', array('limit' => 11)); $table->column('name', 'string', array('limit' => 255, 'default' => '')); $table->column('first_name', 'string', array('limit' => 40, 'default' => '')); $table->column('approved', 'boolean', array('default' => true)); $table->column('type', 'string', array('limit' => 255, 'default' => '')); $table->column('created_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('created_on', 'date', array('default' => '0000-00-00')); $table->column('updated_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('updated_on', 'date', array('default' => '0000-00-00')); $table->end(); } /** * drop test tables */ protected function _dropTestTables() { $tables = array( 'binary_testings', 'cache_table', 'charset_cp1257', 'charset_utf8', 'dates', 'my_sports', 'octopi', 'pk_tests', 'schema_info', 'sports', 'testings', 'text_to_binary', 'unit_tests', 'users', ); foreach ($tables as $table) { try { $this->_conn->dropTable($table); } catch (Exception $e) {} } } protected function _columnNames($tableName) { $columns = array(); foreach ($this->_conn->columns($tableName) as $c) { $columns[] = $c->getName(); } return $columns; } /** * Get a column by name */ protected function _getColumn($table, $column) { foreach ($this->_conn->columns($table) as $col) { if ($col->getName() == $column) return $col; } } /** * Get an index by columns */ protected function _getIndex($table, $indexes) { $indexes = (array) $indexes; sort($indexes); foreach ($this->_conn->indexes($table) as $index) { $columns = $index->columns; sort($columns); if ($columns == $indexes) return $index; } } public function testColumnConstruct() { self::$columnTest->testConstruct(); } public function testColumnToSql() { self::$columnTest->testToSql(); } public function testColumnToSqlLimit() { self::$columnTest->testToSqlLimit(); } public function testColumnToSqlPrecisionScale() { self::$columnTest->testToSqlPrecisionScale(); } public function testColumnToSqlUnsigned() { self::$columnTest->testToSqlUnsigned(); } public function testColumnToSqlNotNull() { self::$columnTest->testToSqlNotNull(); } public function testColumnToSqlDefault() { self::$columnTest->testToSqlDefault(); } public function testTableConstruct() { self::$tableTest->testConstruct(); } public function testTableName() { self::$tableTest->testName(); } public function testTableGetOptions() { self::$tableTest->testGetOptions(); } public function testTablePrimaryKey() { self::$tableTest->testPrimaryKey(); } public function testTableColumn() { self::$tableTest->testColumn(); } public function testTableToSql() { self::$tableTest->testToSql(); } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Pdo/PgsqlBase.php0000664000175000017500000000434212205351473020335 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Pdo_PgsqlBase extends Horde_Test_Case { protected static $skip = true; protected static $reason = 'The PDO_PostgreSQL adapter is not available'; protected static $conn; public static function setUpBeforeClass() { if (extension_loaded('pdo') && in_array('pgsql', PDO::getAvailableDrivers())) { self::$skip = false; self::$conn = self::getConnection(); } } public static function getConnection() { if (!is_null(self::$conn)) { return self::$conn; } $config = Horde_Test_Case::getConfig('DB_ADAPTER_PDO_PGSQL_TEST_CONFIG', null, array('username' => '', 'password' => '', 'dbname' => 'test')); if (isset($config['db']['adapter']['pdo']['pgsql']['test'])) { $config = $config['db']['adapter']['pdo']['pgsql']['test']; } if (!is_array($config)) { self::$skip = true; self::$reason = 'No configuration for pdo_pgsql test'; return; } $conn = new Horde_Db_Adapter_Pdo_Pgsql($config); $cache = new Horde_Cache(new Horde_Cache_Storage_Mock()); $conn->setCache($cache); return array($conn, $cache); } protected function setUp() { if (self::$skip) { $this->markTestSkipped(self::$reason); } } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Pdo/PgsqlTest.php0000664000175000017500000014053512205351473020407 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ require_once __DIR__ . '/PgsqlBase.php'; /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Pdo_PgsqlTest extends Horde_Db_Adapter_Pdo_PgsqlBase { protected function setUp() { parent::setUp(); list($this->_conn, $this->_cache) = self::getConnection(); // clear out detritus from any previous test runs. $this->_dropTestTables(); $table = $this->_conn->createTable('unit_tests'); $table->column('integer_value', 'integer', array('limit' => 11, 'default' => 0)); $table->column('string_value', 'string', array('limit' => 255, 'default' => '')); $table->column('text_value', 'text', array('null' => false, 'default' => '')); $table->column('float_value', 'float', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('decimal_value', 'decimal', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('datetime_value', 'datetime', array()); $table->column('date_value', 'date', array()); $table->column('time_value', 'time', array('default' => '00:00:00')); $table->column('blob_value', 'binary', array('null' => false, 'default' => '')); $table->column('boolean_value', 'boolean', array('default' => false)); $table->column('email_value', 'string', array('limit' => 255, 'default' => '')); $table->end(); $this->_conn->addIndex('unit_tests', 'string_value', array('name' => 'string_value')); $this->_conn->addIndex('unit_tests', 'integer_value', array('name' => 'integer_value', 'unique' => true)); $this->_conn->addIndex('unit_tests', array('integer_value', 'string_value'), array('name' => 'integer_string')); // read sql file for statements $statements = array(); $current_stmt = ''; $fp = fopen(__DIR__ . '/../../fixtures/unit_tests.sql', 'r'); while ($line = fgets($fp, 8192)) { $line = rtrim(preg_replace('/^(.*)--.*$/s', '\1', $line)); if (!$line) { continue; } $current_stmt .= $line; if (substr($line, -1) == ';') { // leave off the ending ; $statements[] = substr($current_stmt, 0, -1); $current_stmt = ''; } } // run statements foreach ($statements as $stmt) { $this->_conn->execute($stmt); } } protected function tearDown() { // clean up $this->_dropTestTables(); } /*########################################################################## # Connection ##########################################################################*/ public function testConnect() { $this->assertTrue($this->_conn->isActive()); } public function testDisconnect() { $this->_conn->disconnect(); $this->assertFalse($this->_conn->isActive()); $this->_conn->connect(); $this->assertTrue($this->_conn->isActive()); } public function testReconnect() { $this->_conn->reconnect(); $this->assertTrue($this->_conn->isActive()); } /*########################################################################## # Accessor ##########################################################################*/ public function testAdapterName() { $this->assertEquals('PDO_PostgreSQL', $this->_conn->adapterName()); } public function testSupportsMigrations() { $this->assertTrue($this->_conn->supportsMigrations()); } public function testSupportsCountDistinct() { $this->assertTrue($this->_conn->supportsCountDistinct()); } /*########################################################################## # Database Statements ##########################################################################*/ public function testExecute() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->execute($sql); $row = $result->fetch(); $this->assertEquals(1, $row['id']); } public function testSelect() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->select($sql); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParameters() { $sql = "SELECT * FROM unit_tests WHERE id=?"; $result = $this->_conn->select($sql, array(1)); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParametersQuotesString() { $sql = "SELECT * FROM unit_tests WHERE string_value=?"; $result = $this->_conn->select($sql, array('name a')); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectAll() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectAll($sql); $this->assertInternalType('array', $result); $this->assertGreaterThan(0, count($result)); $this->assertEquals(1, $result[0]['id']); } public function testSelectOne() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectOne($sql); $this->assertEquals(1, $result['id']); } public function testSelectValue() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectValue($sql); $this->assertEquals(1, $result); } public function testSelectValues() { $sql = "SELECT * FROM unit_tests"; $result = $this->_conn->selectValues($sql); $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result); } public function testInsert() { $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $result = $this->_conn->insert($sql, null, null, null, 7); $this->assertEquals(7, $result); } public function testUpdate() { $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)"; $result = $this->_conn->update($sql); $this->assertEquals(1, $result); } public function testDelete() { $sql = "DELETE FROM unit_tests WHERE id IN (1,2)"; $result = $this->_conn->delete($sql); $this->assertEquals(2, $result); } public function testTransactionStarted() { $this->assertFalse($this->_conn->transactionStarted()); $this->_conn->beginDbTransaction(); $this->assertTrue($this->_conn->transactionStarted()); $this->_conn->commitDbTransaction(); $this->assertFalse($this->_conn->transactionStarted()); } public function testTransactionCommit() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql, null, null, 'id', 7); $this->_conn->commitDbTransaction(); // make sure it inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals('999', $this->_conn->selectValue($sql)); } public function testTransactionRollback() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql, null, null, 'id', 7); $this->_conn->rollbackDbTransaction(); // make sure it not inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals(null, $this->_conn->selectValue($sql)); } /*########################################################################## # Quoting ##########################################################################*/ public function testQuoteNull() { $this->assertEquals('NULL', $this->_conn->quote(null)); } public function testQuoteTrue() { $this->assertEquals("'t'", $this->_conn->quote(true)); } public function testQuoteFalse() { $this->assertEquals("'f'", $this->_conn->quote(false)); } public function testQuoteInteger() { $this->assertEquals('42', $this->_conn->quote(42)); } public function testQuoteFloat() { $this->assertEquals('42.2', $this->_conn->quote(42.2)); setlocale(LC_NUMERIC, 'de_DE.UTF-8'); $this->assertEquals('42.2', $this->_conn->quote(42.2)); } public function testQuoteString() { $this->assertEquals("'my string'", $this->_conn->quote('my string')); } public function testQuoteDirtyString() { $this->assertEquals("'derek''s string'", $this->_conn->quote('derek\'s string')); } public function testQuoteColumnName() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'int(11)'); $this->assertEquals('1', $this->_conn->quote(true, $col)); } public function testQuoteBinary() { // Test string is foo\0bar\baz'boo\'bee - should be 20 bytes long $original = base64_decode('Zm9vAGJhclxiYXonYm9vXCdiZWU='); $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); $this->_conn->insert('INSERT INTO binary_testings (data) VALUES (?)', array(new Horde_Db_Value_Binary($original))); $retrieved = $this->_conn->selectValue('SELECT data FROM binary_testings'); $columns = $this->_conn->columns('binary_testings'); $retrieved = $columns['data']->binaryToString($retrieved); $this->assertEquals($original, $retrieved); } /*########################################################################## # Schema Statements ##########################################################################*/ public function testNativeDatabaseTypes() { $types = $this->_conn->nativeDatabaseTypes(); $this->assertEquals(array('name' => 'integer', 'limit' => null), $types['integer']); } public function testTableAliasLength() { $len = $this->_conn->tableAliasLength(); $this->assertGreaterThanOrEqual(63, $len); } public function testTableAliasFor() { $alias = $this->_conn->tableAliasFor('my_table_name'); $this->assertEquals('my_table_name', $alias); } public function testSchemaSearchPath() { $schemaSearchPath = $this->_conn->getSchemaSearchPath(); $this->assertGreaterThan(0, strlen($schemaSearchPath)); } public function testTables() { $tables = $this->_conn->tables(); $this->assertTrue(count($tables) > 0); $this->assertContains('unit_tests', $tables); } public function testPrimaryKey() { $pk = $this->_conn->primaryKey('unit_tests'); $this->assertEquals('id', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->assertEquals('id', $pk->columns[0]); $table = $this->_conn->createTable('pk_tests', array('autoincrementKey' => false)); $table->column('foo', 'string'); $table->column('bar', 'string'); $table->end(); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', 'foo'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->_conn->removePrimaryKey('pk_tests'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', array('foo', 'bar')); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo,bar', (string)$pk); } public function testIndexes() { $indexes = $this->_conn->indexes('unit_tests'); $this->assertEquals(3, count($indexes)); // sort by name so we can predict the order of indexes usort($indexes, create_function('$a, $b', 'return strcmp($a->name, $b->name);')); // multi-column index $col = array('integer_value', 'string_value'); $this->assertEquals('unit_tests', $indexes[0]->table); $this->assertEquals('integer_string', $indexes[0]->name); $this->assertEquals(false, $indexes[0]->unique); $this->assertEquals($col, $indexes[0]->columns); // unique index $col = array('integer_value'); $this->assertEquals('unit_tests', $indexes[1]->table); $this->assertEquals('integer_value', $indexes[1]->name); $this->assertEquals(true, $indexes[1]->unique); $this->assertEquals($col, $indexes[1]->columns); // normal index $col = array('string_value'); $this->assertEquals('unit_tests', $indexes[2]->table); $this->assertEquals('string_value', $indexes[2]->name); $this->assertEquals(false, $indexes[2]->unique); $this->assertEquals($col, $indexes[2]->columns); } public function testColumns() { $columns = $this->_conn->columns('unit_tests'); $this->assertEquals(12, count($columns)); $col = $columns['id']; $this->assertEquals('id', $col->getName()); $this->assertEquals('integer', $col->getType()); $this->assertEquals(false, $col->isNull()); $this->assertEquals(null, $col->getLimit()); $this->assertEquals('', $col->getDefault()); $this->assertEquals('integer', $col->getSqlType()); $this->assertEquals(false, $col->isText()); $this->assertEquals(true, $col->isNumber()); } public function testCreateTable() { $this->_createTestTable('sports'); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableNoPk() { $this->_createTestTable('sports', array('autoincrementKey' => false)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertNull($this->_conn->selectValue($sql)); } catch (Exception $e) { return; } $this->fail("Expected exception for no pk"); } public function testCreateTableWithNamedPk() { $this->_createTestTable('sports', array('autoincrementKey' => 'sports_id')); $sql = "SELECT sports_id FROM sports WHERE sports_id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertNull($this->_conn->selectValue($sql)); } catch (Exception $e) { return; } $this->fail("Expected exception for wrong pk name"); } public function testCreateTableWithSeparatePk() { $table = $this->_conn->createTable('testings', array('autoincrementKey' => false)); $table->column('foo', 'autoincrementKey'); $table->column('bar', 'integer'); $table->end(); $pkColumn = $table['foo']; $this->assertEquals('"foo" serial primary key', $pkColumn->toSql()); $this->_conn->insert("INSERT INTO testings (bar) VALUES (1)"); $sql = "SELECT * FROM testings WHERE foo = 1"; $result = $this->_conn->select($sql); $this->assertEquals(1, count($result)); // This should fail. try { $this->_conn->insert("INSERT INTO testings (foo) VALUES (NULL)"); $this->fail("Expected exception for inserting null value"); } catch (Exception $e) {} // Manually insert a primary key value. $this->_conn->insert("INSERT INTO testings (foo, bar) VALUES (2, 1)"); $this->_conn->insert("INSERT INTO testings (bar) VALUES (1)"); } public function testAlterTableWithSeparatePk() { $table = $this->_conn->createTable('testings', array('autoincrementKey' => false)); $table->column('foo', 'integer'); $table->column('bar', 'integer'); $table->end(); // Convert 'foo' to auto-increment $this->_conn->changeColumn('testings', 'foo', 'autoincrementKey'); $this->_conn->insert("INSERT INTO testings (bar) VALUES (1)"); $sql = "SELECT * FROM testings WHERE foo = 1"; $result = $this->_conn->select($sql); $this->assertEquals(1, count($result)); } public function testCreateTableCompositePk() { $table = $this->_conn->createTable('testings', array('autoincrementKey' => array('a_id', 'b_id'))); $table->column('a_id', 'integer'); $table->column('b_id', 'integer'); $table->end(); $pk = $this->_conn->primaryKey('testings'); $this->assertEquals(array('a_id', 'b_id'), $pk->columns); } public function testCreateTableForce() { $this->_createTestTable('sports'); $this->_createTestTable('sports', array('force' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableTemporary() { $this->_createTestTable('sports', array('temporary' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableAddsId() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[] = $col->getName(); } sort($columns); $this->assertEquals(array('foo', 'id'), $columns); } public function testCreateTableWithNotNullColumn() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('null' => false)); $table->end(); try { $this->_conn->insert("INSERT INTO testings (foo) VALUES (NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testCreateTableWithDefaults() { $table = $this->_conn->createTable('testings'); $table->column('one', 'string', array('default' => 'hello')); $table->column('two', 'boolean', array('default' => true)); $table->column('three', 'boolean', array('default' => false)); $table->column('four', 'integer', array('default' => 1)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals('hello', $columns['one']->getDefault()); $this->assertTrue($columns['two']->getDefault()); $this->assertFalse($columns['three']->getDefault()); $this->assertEquals(1, $columns['four']->getDefault()); } public function testCreateTableWithLimits() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('limit' => 80)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals(80, $columns['foo']->getLimit()); } public function testCreateTableWithBinaryColumn() { try { $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); } catch (Exception $e) { $this->fail('Unexepected exception raised'); } $columns = $this->_conn->columns('binary_testings'); foreach ($columns as $c) { if ($c->getName() == 'data') { $dataColumn = $c; } } $this->assertEquals('', $dataColumn->getDefault()); } public function testRenameTable() { // Simple rename then select test $this->_createTestTable('sports'); $this->_conn->renameTable('sports', 'my_sports'); $sql = "SELECT id FROM my_sports WHERE id = 1"; $this->assertEquals("1", $this->_conn->selectValue($sql)); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); // Rename then insert test $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->insert($sql); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM octopuses WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testRenameTableWithAnIndex() { $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->addIndex('octopuses', 'url'); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->insert($sql, null, null, 'id', 1); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); $indexes = $this->_conn->indexes('octopi'); $this->assertEquals('url', $indexes[0]->columns[0]); } public function testDropTable() { $this->_createTestTable('sports'); $this->_conn->dropTable('sports'); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testAddColumn() { $this->_createTestTable('sports'); $this->_conn->addColumn('sports', 'modified_at', 'date'); $this->_conn->update("UPDATE sports SET modified_at = '2007-01-01'"); $sql = "SELECT modified_at FROM sports WHERE id = 1"; $this->assertEquals("2007-01-01", $this->_conn->selectValue($sql)); $this->_conn->addColumn('sports', 'with_default', 'integer', array('default' => 1)); $sql = "SELECT with_default FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testRemoveColumn() { $this->_createTestTable('sports'); $sql = "SELECT name FROM sports WHERE id = 1"; $this->assertEquals("mlb", $this->_conn->selectValue($sql)); $this->_conn->removeColumn('sports', 'name'); try { $sql = "SELECT name FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Column exists where it shouldn't have"); } public function testChangeColumn() { $this->_createTestUsersTable(); $this->_conn->addColumn('users', 'age', 'integer'); $oldColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertTrue($found); $this->_conn->changeColumn('users', 'age', 'string'); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'string') { $found = true; } } $this->assertTrue($found); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => false)); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == false) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => true)); } public function testChangeColumnDefault() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'name'); $this->assertEquals('', $beforeChange->getDefault()); $this->_conn->changeColumnDefault('sports', 'name', 'test'); $afterChange = $this->_getColumn('sports', 'name'); $this->assertEquals('test', $afterChange->getDefault()); } public function testChangeColumnType() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('boolean', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string'); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('character varying(255)', $afterChange->getSqlType()); $table = $this->_conn->createTable('text_to_binary'); $table->column('data', 'text'); $table->end(); $this->_conn->insert('INSERT INTO text_to_binary (data) VALUES (?)', array("foobar")); $this->_conn->changeColumn('text_to_binary', 'data', 'binary'); $afterChange = $this->_getColumn('text_to_binary', 'data'); $this->assertEquals('bytea', $afterChange->getSqlType()); $this->assertEquals( "foobar", stream_get_contents($this->_conn->selectValue('SELECT data FROM text_to_binary'))); } public function testChangeColumnLimit() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('boolean', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string', array('limit' => '40')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('character varying(40)', $afterChange->getSqlType()); } public function testChangeColumnPrecisionScale() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('boolean', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'decimal', array('precision' => '5', 'scale' => '2')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('numeric(5,2)', $afterChange->getSqlType()); } public function testRenameColumn() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'nick_name'); $this->assertTrue(in_array('nick_name', $this->_columnNames('users'))); $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('boolean', $beforeChange->getSqlType()); $this->_conn->renameColumn('sports', 'is_college', 'is_renamed'); $afterChange = $this->_getColumn('sports', 'is_renamed'); $this->assertEquals('boolean', $afterChange->getSqlType()); } public function testRenameColumnWithSqlReservedWord() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'group'); $this->assertTrue(in_array('group', $this->_columnNames('users'))); } public function testAddIndex() { $this->_createTestUsersTable(); // Limit size of last_name and key columns to support Firebird index limitations $this->_conn->addColumn('users', 'last_name', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'key', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'administrator', 'boolean'); $this->_conn->addIndex('users', 'last_name'); $this->_conn->removeIndex('users', 'last_name'); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('column' => array('last_name', 'first_name'))); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('name' => 'index_users_on_last_name_and_first_name')); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', 'last_name_and_first_name'); // quoting $this->_conn->addIndex('users', array('key'), array('name' => 'key_idx', 'unique' => true)); $this->_conn->removeIndex('users', array('name' => 'key_idx', 'unique' => true)); $this->_conn->addIndex('users', array('last_name', 'first_name', 'administrator'), array('name' => "named_admin")); $this->_conn->removeIndex('users', array('name' => 'named_admin')); } public function testAddIndexDefault() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); } public function testAddIndexMultiColumn() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); } public function testAddIndexUnique() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('unique' => true)); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertTrue($index->unique); } public function testAddIndexName() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('name' => 'sports_test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertEquals('sports_test', $index->name); } public function testRemoveIndexSingleColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => 'is_college')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testRemoveIndexMultiColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => array('name', 'is_college'))); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); } public function testRemoveIndexByName() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college', array('name' => 'sports_test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('name' => 'sports_test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testIndexNameInvalid() { try { $name = $this->_conn->indexName('sports'); } catch (Horde_Db_Exception $e) { return; } $this->fail("Adding an index with crappy options worked where it shouldn't have"); } public function testIndexNameBySingleColumn() { $name = $this->_conn->indexName('sports', array('column' => 'is_college')); $this->assertEquals('index_sports_on_is_college', $name); } public function testIndexNameByMultiColumn() { $name = $this->_conn->indexName('sports', array('column' => array('name', 'is_college'))); $this->assertEquals('index_sports_on_name_and_is_college', $name); } public function testIndexNameByName() { $name = $this->_conn->indexName('sports', array('name' => 'test')); $this->assertEquals('test', $name); } public function testTypeToSqlTypePrimaryKey() { $result = $this->_conn->typeToSql('autoincrementKey'); $this->assertEquals('serial primary key', $result); } public function testTypeToSqlTypeString() { $result = $this->_conn->typeToSql('string'); $this->assertEquals('character varying(255)', $result); } public function testTypeToSqlTypeText() { $result = $this->_conn->typeToSql('text'); $this->assertEquals('text', $result); } public function testTypeToSqlTypeBinary() { $result = $this->_conn->typeToSql('binary'); $this->assertEquals('bytea', $result); } public function testTypeToSqlTypeFloat() { $result = $this->_conn->typeToSql('float'); $this->assertEquals('float', $result); } public function testTypeToSqlTypeDatetime() { $result = $this->_conn->typeToSql('datetime'); $this->assertEquals('timestamp', $result); } public function testTypeToSqlTypeTimestamp() { $result = $this->_conn->typeToSql('timestamp'); $this->assertEquals('timestamp', $result); } public function testTypeToSqlInt() { $result = $this->_conn->typeToSql('integer'); $this->assertEquals('integer', $result); } public function testTypeToSqlIntLimit() { $result = $this->_conn->typeToSql('integer', '1'); $this->assertEquals('smallint', $result); } public function testTypeToSqlDecimalPrecision() { $result = $this->_conn->typeToSql('decimal', null, '5'); $this->assertEquals('decimal(5)', $result); } public function testTypeToSqlDecimalScale() { $result = $this->_conn->typeToSql('decimal', null, '5', '2'); $this->assertEquals('decimal(5, 2)', $result); } public function testTypeToSqlBoolean() { $result = $this->_conn->typeToSql('boolean'); $this->assertEquals('boolean', $result); } public function testAddColumnOptions() { $result = $this->_conn->addColumnOptions("test", array()); $this->assertEquals("test", $result); } public function testAddColumnOptionsDefault() { $options = array('default' => '0'); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test DEFAULT '0'", $result); } public function testAddColumnOptionsNull() { $options = array('null' => true); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test", $result); } public function testAddColumnOptionsNotNull() { $options = array('null' => false); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test NOT NULL", $result); } public function testAddColumnNotNullWithoutDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => '')); try { $this->_conn->insert("INSERT INTO testings (foo, bar) VALUES ('hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddColumnNotNullWithDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->insert("INSERT INTO testings (id, foo) VALUES ('1', 'hello')", null, null, 'id', 1); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => 'default')); try { $this->_conn->insert("INSERT INTO testings (id, foo, bar) VALUES (2, 'hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddRemoveSingleField() { $this->_createTestUsersTable(); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); $this->_conn->addColumn('users', 'last_name', 'string'); $this->assertTrue(in_array('last_name', $this->_columnNames('users'))); $this->_conn->removeColumn('users', 'last_name'); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); } public function testAddRename() { $this->_createTestUsersTable(); $this->_conn->delete('DELETE FROM users'); $this->_conn->addColumn('users', 'girlfriend', 'string'); $this->_conn->insert("INSERT INTO users (girlfriend) VALUES ('bobette')"); $this->_conn->renameColumn('users', 'girlfriend', 'exgirlfriend'); $bob = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('bobette', $bob->exgirlfriend); } public function testDistinct() { $result = $this->_conn->distinct("test"); $this->assertEquals("DISTINCT test", $result); } public function testAddOrderByForAssocLimiting() { $result = $this->_conn->addOrderByForAssocLimiting("SELECT * FROM documents ", array('order' => 'name DESC')); $this->assertEquals("SELECT * FROM documents ORDER BY name DESC", $result); } public function testInterval() { $this->assertEquals('INTERVAL \'1 DAY \'', $this->_conn->interval('1 DAY', '')); } public function testModifyDate() { $modifiedDate = $this->_conn->modifyDate('mystart', '+', 1, 'DAY'); $this->assertEquals('mystart + INTERVAL \'1 DAY\'', $modifiedDate); $t = $this->_conn->createTable('dates'); $t->column('mystart', 'datetime'); $t->column('myend', 'datetime'); $t->end(); $this->_conn->insert( 'INSERT INTO dates (mystart, myend) VALUES (?, ?)', array( '2011-12-10 00:00:00', '2011-12-11 00:00:00' ) ); $this->assertEquals( 1, $this->_conn->selectValue('SELECT COUNT(*) FROM dates WHERE ' . $modifiedDate . ' = myend') ); } public function testBuildClause() { $this->assertEquals( "CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) & 2) <> 0 ELSE FALSE END", $this->_conn->buildClause('bitmap', '&', 2)); $this->assertEquals( array("CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) & ?) <> 0 ELSE FALSE END", array(2)), $this->_conn->buildClause('bitmap', '&', 2, true)); $this->assertEquals( "CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) | 2) <> 0 ELSE FALSE END", $this->_conn->buildClause('bitmap', '|', 2)); $this->assertEquals( array("CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) | ?) <> 0 ELSE FALSE END", array(2)), $this->_conn->buildClause('bitmap', '|', 2, true)); $this->assertEquals( "name ILIKE '%search%'", $this->_conn->buildClause('name', 'LIKE', "search")); $this->assertEquals( array("name ILIKE ?", array('%search%')), $this->_conn->buildClause('name', 'LIKE', "search", true)); $this->assertEquals( "name ILIKE '%search\&replace\?%'", $this->_conn->buildClause('name', 'LIKE', "search&replace?")); $this->assertEquals( array("name ILIKE ?", array('%search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true)); $this->assertEquals( "(name ILIKE 'search\&replace\?%' OR name ILIKE '% search\&replace\?%')", $this->_conn->buildClause('name', 'LIKE', "search&replace?", false, array('begin' => true))); $this->assertEquals( array("(name ILIKE ? OR name ILIKE ?)", array('search&replace?%', '% search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true, array('begin' => true))); $this->assertEquals( 'value = 2', $this->_conn->buildClause('value', '=', 2)); $this->assertEquals( array('value = ?', array(2)), $this->_conn->buildClause('value', '=', 2, true)); $this->assertEquals( "value = 'foo'", $this->_conn->buildClause('value', '=', 'foo')); $this->assertEquals( array('value = ?', array('foo')), $this->_conn->buildClause('value', '=', 'foo', true)); $this->assertEquals( "value = 'foo\?bar'", $this->_conn->buildClause('value', '=', 'foo?bar')); $this->assertEquals( array('value = ?', array('foo?bar')), $this->_conn->buildClause('value', '=', 'foo?bar', true)); } /*########################################################################## # Table cache ##########################################################################*/ public function testCachedTableIndexes() { // remove any current cache. $this->_cache->set('tables/indexes/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/indexes/cache_table', 0)); $this->_createTestTable('cache_table'); $idxs = $this->_conn->indexes('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/indexes/cache_table', 0)); } public function testCachedTableColumns() { // remove any current cache. $this->_cache->set('tables/columns/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/columns/cache_table'), 0); $this->_createTestTable('cache_table'); $cols = $this->_conn->columns('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/columns/cache_table', 0)); } /*########################################################################## # Protected ##########################################################################*/ /** * Create table to perform tests on */ protected function _createTestTable($name, $options=array()) { $table = $this->_conn->createTable($name, $options); $table->column('name', 'string'); $table->column('is_college', 'boolean'); $table->end(); try { // make sure table was created $sql = "INSERT INTO $name VALUES (1, 'mlb', 'f')"; $this->_conn->insert($sql); } catch (Exception $e) {} } protected function _createTestUsersTable() { $table = $this->_conn->createTable('users'); $table->column('company_id', 'integer', array('limit' => 11)); $table->column('name', 'string', array('limit' => 255, 'default' => '')); $table->column('first_name', 'string', array('limit' => 40, 'default' => '')); $table->column('approved', 'boolean', array('default' => true)); $table->column('type', 'string', array('limit' => 255, 'default' => '')); $table->column('created_at', 'datetime', array()); $table->column('created_on', 'date', array()); $table->column('updated_at', 'datetime', array()); $table->column('updated_on', 'date', array()); $table->end(); } /** * drop test tables */ protected function _dropTestTables() { $tables = array( 'binary_testings', 'cache_table', 'dates', 'my_sports', 'octopi', 'pk_tests', 'schema_info', 'sports', 'testings', 'text_to_binary', 'unit_tests', 'users', ); foreach ($tables as $table) { try { $this->_conn->dropTable($table); } catch (Exception $e) {} } } protected function _columnNames($tableName) { $columns = array(); foreach ($this->_conn->columns($tableName) as $c) { $columns[] = $c->getName(); } return $columns; } /** * Get a column by name */ protected function _getColumn($table, $column) { foreach ($this->_conn->columns($table) as $col) { if ($col->getName() == $column) return $col; } } /** * Get an index by columns */ protected function _getIndex($table, $indexes) { $indexes = (array) $indexes; sort($indexes); foreach ($this->_conn->indexes($table) as $index) { $columns = $index->columns; sort($columns); if ($columns == $indexes) return $index; } } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Pdo/SqliteBase.php0000664000175000017500000000335412205351473020512 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Pdo_SqliteBase extends Horde_Test_Case { protected static $skip = true; protected static $reason = 'The PDO_SQLite adapter is not available'; public static function setUpBeforeClass() { if (extension_loaded('pdo') && in_array('sqlite', PDO::getAvailableDrivers())) { self::$skip = false; list($conn,) = self::getConnection(); $conn->disconnect(); } } public static function getConnection($overrides = array()) { $config = array( 'dbname' => ':memory:', ); $config = array_merge($config, $overrides); $conn = new Horde_Db_Adapter_Pdo_Sqlite($config); $cache = new Horde_Cache(new Horde_Cache_Storage_Mock()); $conn->setCache($cache); return array($conn, $cache); } protected function setUp() { if (self::$skip) { $this->markTestSkipped(self::$reason); } } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Pdo/SqliteTest.php0000664000175000017500000014144412205351473020562 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ require_once __DIR__ . '/SqliteBase.php'; /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Pdo_SqliteTest extends Horde_Db_Adapter_Pdo_SqliteBase { protected function setUp() { parent::setUp(); list($this->_conn, $this->_cache) = self::getConnection(); $table = $this->_conn->createTable('unit_tests'); $table->column('integer_value', 'integer', array('limit' => 11, 'default' => 0)); $table->column('string_value', 'string', array('limit' => 255, 'default' => '')); $table->column('text_value', 'text', array('null' => false, 'default' => '')); $table->column('float_value', 'float', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('decimal_value', 'decimal', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('datetime_value', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('date_value', 'date', array('default' => '0000-00-00')); $table->column('time_value', 'time', array('default' => '00:00:00')); $table->column('blob_value', 'binary', array('null' => false, 'default' => '')); $table->column('boolean_value', 'boolean', array('default' => false)); $table->column('email_value', 'string', array('limit' => 255, 'default' => '')); $table->end(); $this->_conn->addIndex('unit_tests', 'string_value', array('name' => 'string_value')); $this->_conn->addIndex('unit_tests', 'integer_value', array('name' => 'integer_value', 'unique' => true)); $this->_conn->addIndex('unit_tests', array('integer_value', 'string_value'), array('name' => 'integer_string')); // read sql file for statements $statements = array(); $current_stmt = ''; $fp = fopen(__DIR__ . '/../../fixtures/unit_tests.sql', 'r'); while ($line = fgets($fp, 8192)) { $line = rtrim(preg_replace('/^(.*)--.*$/s', '\1', $line)); if (!$line) { continue; } $current_stmt .= $line; if (substr($line, -1) == ';') { // leave off the ending ; $statements[] = substr($current_stmt, 0, -1); $current_stmt = ''; } } // run statements foreach ($statements as $stmt) { $this->_conn->execute($stmt); } } protected function tearDown() { // close connection $this->_conn->disconnect(); } /*########################################################################## # Connection ##########################################################################*/ public function testConnect() { $this->assertTrue($this->_conn->isActive()); } public function testDisconnect() { $this->_conn->disconnect(); $this->assertFalse($this->_conn->isActive()); $this->_conn->connect(); $this->assertTrue($this->_conn->isActive()); } public function testReconnect() { $this->_conn->reconnect(); $this->assertTrue($this->_conn->isActive()); } /*########################################################################## # Accessor ##########################################################################*/ public function testAdapterName() { $this->assertEquals('PDO_SQLite', $this->_conn->adapterName()); } public function testSupportsMigrations() { $this->assertTrue($this->_conn->supportsMigrations()); } public function testSupportsCountDistinct() { $version = $this->_conn->selectValue('SELECT sqlite_version(*)'); if ($version >= '3.2.6') { $this->assertTrue($this->_conn->supportsCountDistinct()); } else { $this->assertFalse($this->_conn->supportsCountDistinct()); } } /*########################################################################## # Database Statements ##########################################################################*/ public function testExecute() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->execute($sql); $row = $result->fetch(); $this->assertEquals(1, $row['id']); } public function testSelect() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->select($sql); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParameters() { $sql = "SELECT * FROM unit_tests WHERE id=?"; $result = $this->_conn->select($sql, array(1)); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParametersQuotesString() { $sql = "SELECT * FROM unit_tests WHERE string_value=?"; $result = $this->_conn->select($sql, array('name a')); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectAll() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectAll($sql); $this->assertInternalType('array', $result); $this->assertGreaterThan(0, count($result)); $this->assertEquals(1, $result[0]['id']); } public function testSelectOne() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectOne($sql); $this->assertArrayHasKey('id', $result); $this->assertEquals(1, $result['id']); } public function testSelectValue() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectValue($sql); $this->assertEquals(1, $result); } public function testSelectValues() { $sql = "SELECT * FROM unit_tests"; $result = $this->_conn->selectValues($sql); $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result); } public function testInsert() { $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $result = $this->_conn->insert($sql); $this->assertEquals(7, $result); } public function testUpdate() { $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)"; $result = $this->_conn->update($sql); $this->assertEquals(1, $result); } public function testDelete() { $sql = "DELETE FROM unit_tests WHERE id IN (1,2)"; $result = $this->_conn->delete($sql); $this->assertEquals(2, $result); } public function testTransactionStarted() { $this->assertFalse($this->_conn->transactionStarted()); $this->_conn->beginDbTransaction(); $this->assertTrue($this->_conn->transactionStarted()); $this->_conn->commitDbTransaction(); $this->assertFalse($this->_conn->transactionStarted()); } public function testTransactionCommit() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql); $this->_conn->commitDbTransaction(); // make sure it inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals('999', $this->_conn->selectValue($sql)); } public function testTransactionRollback() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql); $this->_conn->rollbackDbTransaction(); // make sure it inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals(null, $this->_conn->selectValue($sql)); } /*########################################################################## # Quoting ##########################################################################*/ public function testQuoteNull() { $this->assertEquals('NULL', $this->_conn->quote(null)); } public function testQuoteTrue() { $this->assertEquals('1', $this->_conn->quote(true)); } public function testQuoteFalse() { $this->assertEquals('0', $this->_conn->quote(false)); } public function testQuoteInteger() { $this->assertEquals('42', $this->_conn->quote(42)); } public function testQuoteFloat() { $this->assertEquals('42.2', $this->_conn->quote(42.2)); setlocale(LC_NUMERIC, 'de_DE.UTF-8'); $this->assertEquals('42.2', $this->_conn->quote(42.2)); } public function testQuoteString() { $this->assertEquals("'my string'", $this->_conn->quote('my string')); } public function testQuoteDirtyString() { $this->assertEquals("'derek''s string'", $this->_conn->quote('derek\'s string')); } public function testQuoteColumnName() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'int(11)'); $this->assertEquals('1', $this->_conn->quote(true, $col)); } public function testQuoteBinary() { // Test string is foo\0ba'r - should be 8 bytes long $original = base64_decode('Zm9vAGJhJ3I='); $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); $this->_conn->insert('INSERT INTO binary_testings (data) VALUES (?)', array(new Horde_Db_Value_Binary($original))); $retrieved = $this->_conn->selectValue('SELECT data FROM binary_testings'); $columns = $this->_conn->columns('binary_testings'); $retrieved = $columns['data']->binaryToString($retrieved); $this->assertEquals($original, $retrieved); } /*########################################################################## # Schema Statements ##########################################################################*/ public function testNativeDatabaseTypes() { $types = $this->_conn->nativeDatabaseTypes(); $this->assertEquals(array('name' => 'int', 'limit' => null), $types['integer']); } public function testTableAliasLength() { $len = $this->_conn->tableAliasLength(); $this->assertEquals(255, $len); } public function testTableAliasFor() { $alias = $this->_conn->tableAliasFor('my_table_name'); $this->assertEquals('my_table_name', $alias); } public function testTables() { $tables = $this->_conn->tables(); $this->assertTrue(count($tables) > 0); $this->assertContains('unit_tests', $tables); } public function testPrimaryKey() { $pk = $this->_conn->primaryKey('unit_tests'); $this->assertEquals('id', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->assertEquals('id', $pk->columns[0]); $table = $this->_conn->createTable('pk_tests', array('autoincrementKey' => false)); $table->column('foo', 'string'); $table->column('bar', 'string'); $table->end(); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', 'foo'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->_conn->removePrimaryKey('pk_tests'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', array('foo', 'bar')); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo,bar', (string)$pk); } public function testIndexes() { $indexes = $this->_conn->indexes('unit_tests'); $this->assertEquals(3, count($indexes)); // sort indexes alphabetically usort($indexes, create_function('$a, $b', 'return strcmp($a->name, $b->name);')); // multi-column index $col = array('integer_value', 'string_value'); $this->assertEquals('unit_tests', $indexes[0]->table); $this->assertEquals('integer_string', $indexes[0]->name); $this->assertEquals(false, $indexes[0]->unique); $this->assertEquals($col, $indexes[0]->columns); // unique index $col = array('integer_value'); $this->assertEquals('unit_tests', $indexes[1]->table); $this->assertEquals('integer_value', $indexes[1]->name); $this->assertEquals(true, $indexes[1]->unique); $this->assertEquals($col, $indexes[1]->columns); // normal index $col = array('string_value'); $this->assertEquals('unit_tests', $indexes[2]->table); $this->assertEquals('string_value', $indexes[2]->name); $this->assertEquals(false, $indexes[2]->unique); $this->assertEquals($col, $indexes[2]->columns); } public function testColumns() { $columns = $this->_conn->columns('unit_tests'); $this->assertEquals(12, count($columns)); $col = $columns['id']; $this->assertEquals('id', $col->getName()); $this->assertEquals('integer', $col->getType()); $this->assertEquals(false, $col->isNull()); $this->assertEquals(null, $col->getLimit()); $this->assertEquals('', $col->getDefault()); $this->assertEquals('INTEGER', $col->getSqlType()); $this->assertEquals(false, $col->isText()); $this->assertEquals(true, $col->isNumber()); } public function testCreateTable() { $this->_createTestTable('sports'); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableNoPk() { $this->_createTestTable('sports', array('autoincrementKey' => false)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertNull($this->_conn->selectValue($sql)); } catch (Exception $e) { return; } $this->fail("Expected exception for no pk"); } public function testCreateTableWithNamedPk() { $this->_createTestTable('sports', array('autoincrementKey' => 'sports_id')); $sql = "SELECT sports_id FROM sports WHERE sports_id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->selectValue($sql); $this->fail("Expected exception for wrong pk name"); } catch (Exception $e) { } $sql = "INSERT INTO sports ('name', 'is_college') VALUES ('foo', 1)"; $this->assertEquals(2, $this->_conn->insert($sql)); } public function testCreateTableWithSeparatePk() { $table = $this->_conn->createTable('testings', array('autoincrementKey' => false)); $table->column('foo', 'autoincrementKey'); $table->column('bar', 'string'); $pkColumn = $table['foo']; $this->assertEquals('"foo" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL', $pkColumn->toSql()); $table->end(); $sql = "INSERT INTO testings ('bar') VALUES ('baz')"; $this->assertEquals(1, $this->_conn->insert($sql)); $this->assertEquals(2, $this->_conn->insert($sql)); } public function testCreateTableWithSeparatePk2() { $table = $this->_conn->createTable('testings', array('autoincrementKey' => false)); $table->column('foo', 'integer', array('null' => false, 'autoincrement' => true)); $table->column('bar', 'string'); $table->primaryKey('foo'); $pkColumn = $table['foo']; $this->assertEquals('"foo" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL', $pkColumn->toSql()); $table->end(); $sql = "INSERT INTO testings ('bar') VALUES ('baz')"; $this->assertEquals(1, $this->_conn->insert($sql)); $this->assertEquals(2, $this->_conn->insert($sql)); } public function testCreateTableCompositePk() { $table = $this->_conn->createTable('testings', array('autoincrementKey' => array('a_id', 'b_id'))); $table->column('a_id', 'integer'); $table->column('b_id', 'integer'); $table->end(); $pk = $this->_conn->primaryKey('testings'); $this->assertEquals(array('a_id', 'b_id'), $pk->columns); } public function testCreateTableForce() { $this->_createTestTable('sports'); $this->_createTestTable('sports', array('force' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableTemporary() { $this->_createTestTable('sports', array('temporary' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableAddsId() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[] = $col->getName(); } sort($columns); $this->assertEquals(array('foo', 'id'), $columns); } public function testCreateTableWithNotNullColumn() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('null' => false)); $table->end(); try { $this->_conn->execute("INSERT INTO testings (foo) VALUES (NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testCreateTableWithDefaults() { $table = $this->_conn->createTable('testings'); $table->column('one', 'string', array('default' => 'hello')); $table->column('two', 'boolean', array('default' => true)); $table->column('three', 'boolean', array('default' => false)); $table->column('four', 'integer', array('default' => 1)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals('hello', $columns['one']->getDefault()); $this->assertTrue($columns['two']->getDefault()); $this->assertFalse($columns['three']->getDefault()); $this->assertEquals(1, $columns['four']->getDefault()); } public function testCreateTableWithLimits() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('limit' => 80)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals(80, $columns['foo']->getLimit()); } public function testCreateTableWithBinaryColumn() { try { $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); } catch (Exception $e) { $this->fail('Unexepected exception raised'); } $columns = $this->_conn->columns('binary_testings'); foreach ($columns as $c) { if ($c->getName() == 'data') { $dataColumn = $c; } } $this->assertEquals('', $dataColumn->getDefault()); } public function testRenameTable() { // Simple rename then select test $this->_createTestTable('sports'); $this->_conn->renameTable('sports', 'my_sports'); $sql = "SELECT id FROM my_sports WHERE id = 1"; $this->assertEquals("1", $this->_conn->selectValue($sql)); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); // Rename then insert test $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->execute($sql); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM octopuses WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testRenameTableWithAnIndex() { $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->addIndex('octopuses', 'url'); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->execute($sql); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); $indexes = $this->_conn->indexes('octopi'); $this->assertEquals('url', $indexes[0]->columns[0]); } public function testDropTable() { $this->_createTestTable('sports'); $this->_conn->dropTable('sports'); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testAddColumn() { $this->_createTestTable('sports'); $this->_conn->addColumn('sports', 'modified_at', 'date'); $this->_conn->update("UPDATE sports SET modified_at = '2007-01-01'"); $sql = "SELECT modified_at FROM sports WHERE id = 1"; $this->assertEquals("2007-01-01", $this->_conn->selectValue($sql)); $this->_conn->addColumn('sports', 'with_default', 'integer', array('default' => 1)); $sql = "SELECT with_default FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testRemoveColumn() { $this->_createTestTable('sports'); $sql = "SELECT name FROM sports WHERE id = 1"; $this->assertEquals("mlb", $this->_conn->selectValue($sql)); $this->_conn->removeColumn('sports', 'name'); try { $sql = "SELECT name FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Column exists where it shouldn't have"); } public function testChangeColumn() { $this->_createTestUsersTable(); $this->_conn->addColumn('users', 'age', 'integer'); $oldColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertTrue($found); $this->_conn->changeColumn('users', 'age', 'string'); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'string') { $found = true; } } $this->assertTrue($found); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => false)); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == false) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => true)); } public function testChangeColumnDefault() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'name'); $this->assertEquals('', $beforeChange->getDefault()); $this->_conn->changeColumnDefault('sports', 'name', 'test'); $afterChange = $this->_getColumn('sports', 'name'); $this->assertEquals('test', $afterChange->getDefault()); } public function testChangeColumnType() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('boolean', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string'); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar(255)', $afterChange->getSqlType()); $table = $this->_conn->createTable('text_to_binary'); $table->column('data', 'text'); $table->end(); $this->_conn->insert('INSERT INTO text_to_binary (data) VALUES (?)', array("foobar")); $this->_conn->changeColumn('text_to_binary', 'data', 'binary'); $afterChange = $this->_getColumn('text_to_binary', 'data'); $this->assertEquals('blob', $afterChange->getSqlType()); $this->assertEquals( "foobar", $this->_conn->selectValue('SELECT data FROM text_to_binary')); } public function testChangeColumnLimit() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('boolean', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string', array('limit' => '40')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar(40)', $afterChange->getSqlType()); } public function testChangeColumnPrecisionScale() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('boolean', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'decimal', array('precision' => '5', 'scale' => '2')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertRegExp('/^decimal\(5,\s*2\)$/', $afterChange->getSqlType()); } public function testRenameColumn() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'nick_name'); $this->assertTrue(in_array('nick_name', $this->_columnNames('users'))); $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('boolean', $beforeChange->getSqlType()); $this->_conn->renameColumn('sports', 'is_college', 'is_renamed'); $afterChange = $this->_getColumn('sports', 'is_renamed'); $this->assertEquals('boolean', $afterChange->getSqlType()); } public function testRenameColumnWithSqlReservedWord() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'group'); $this->assertTrue(in_array('group', $this->_columnNames('users'))); } public function testAddIndex() { $this->_createTestUsersTable(); // Limit size of last_name and key columns to support Firebird index limitations $this->_conn->addColumn('users', 'last_name', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'key', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'administrator', 'boolean'); $this->_conn->addIndex('users', 'last_name'); $this->_conn->removeIndex('users', 'last_name'); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('column' => array('last_name', 'first_name'))); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('name' => 'index_users_on_last_name_and_first_name')); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', 'last_name_and_first_name'); // quoting $this->_conn->addIndex('users', array('key'), array('name' => 'key_idx', 'unique' => true)); $this->_conn->removeIndex('users', array('name' => 'key_idx', 'unique' => true)); $this->_conn->addIndex('users', array('last_name', 'first_name', 'administrator'), array('name' => "named_admin")); $this->_conn->removeIndex('users', array('name' => 'named_admin')); } public function testAddIndexDefault() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); } public function testAddIndexMultiColumn() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); } public function testAddIndexUnique() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('unique' => true)); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertTrue($index->unique); } public function testAddIndexName() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertEquals('test', $index->name); } public function testRemoveIndexSingleColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => 'is_college')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testRemoveIndexMultiColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => array('name', 'is_college'))); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); } public function testRemoveIndexByName() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testIndexNameInvalid() { try { $name = $this->_conn->indexName('sports'); } catch (Horde_Db_Exception $e) { return; } $this->fail("Adding an index with crappy options worked where it shouldn't have"); } public function testIndexNameBySingleColumn() { $name = $this->_conn->indexName('sports', array('column' => 'is_college')); $this->assertEquals('index_sports_on_is_college', $name); } public function testIndexNameByMultiColumn() { $name = $this->_conn->indexName('sports', array('column' => array('name', 'is_college'))); $this->assertEquals('index_sports_on_name_and_is_college', $name); } public function testIndexNameByName() { $name = $this->_conn->indexName('sports', array('name' => 'test')); $this->assertEquals('test', $name); } public function testTypeToSqlTypePrimaryKey() { $result = $this->_conn->typeToSql('autoincrementKey'); $this->assertEquals('INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL', $result); } public function testTypeToSqlTypeString() { $result = $this->_conn->typeToSql('string'); $this->assertEquals('varchar(255)', $result); } public function testTypeToSqlTypeText() { $result = $this->_conn->typeToSql('text'); $this->assertEquals('text', $result); } public function testTypeToSqlTypeBinary() { $result = $this->_conn->typeToSql('binary'); $this->assertEquals('blob', $result); } public function testTypeToSqlTypeFloat() { $result = $this->_conn->typeToSql('float'); $this->assertEquals('float', $result); } public function testTypeToSqlTypeDatetime() { $result = $this->_conn->typeToSql('datetime'); $this->assertEquals('datetime', $result); } public function testTypeToSqlTypeTimestamp() { $result = $this->_conn->typeToSql('timestamp'); $this->assertEquals('datetime', $result); } public function testTypeToSqlInt() { $result = $this->_conn->typeToSql('integer'); $this->assertEquals('int', $result); } public function testTypeToSqlIntLimit() { $result = $this->_conn->typeToSql('integer', '1'); $this->assertEquals('int(1)', $result); } public function testTypeToSqlDecimalPrecision() { $result = $this->_conn->typeToSql('decimal', null, '5'); $this->assertEquals('decimal(5)', $result); } public function testTypeToSqlDecimalScale() { $result = $this->_conn->typeToSql('decimal', null, '5', '2'); $this->assertEquals('decimal(5, 2)', $result); } public function testTypeToSqlBoolean() { $result = $this->_conn->typeToSql('boolean'); $this->assertEquals('boolean', $result); } public function testAddColumnOptions() { $result = $this->_conn->addColumnOptions('test', array()); $this->assertEquals('test', $result); } public function testAddColumnOptionsDefault() { $options = array('default' => '0'); $result = $this->_conn->addColumnOptions('test', $options); $this->assertEquals("test DEFAULT '0'", $result); } public function testAddColumnOptionsNull() { $options = array('null' => true); $result = $this->_conn->addColumnOptions('test', $options); $this->assertEquals('test', $result); } public function testAddColumnOptionsNotNull() { $options = array('null' => false); $result = $this->_conn->addColumnOptions('test', $options); $this->assertEquals('test NOT NULL', $result); } public function testAddColumnNotNullWithoutDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => '')); try { $this->_conn->execute("INSERT INTO testings (foo, bar) VALUES ('hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddColumnNotNullWithDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->execute("INSERT INTO testings (id, foo) VALUES ('1', 'hello')"); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => 'default')); try { $this->_conn->execute("INSERT INTO testings (id, foo, bar) VALUES (2, 'hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddRemoveSingleField() { $this->_createTestUsersTable(); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); $this->_conn->addColumn('users', 'last_name', 'string'); $this->assertTrue(in_array('last_name', $this->_columnNames('users'))); $this->_conn->removeColumn('users', 'last_name'); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); } public function testAddRename() { $this->_createTestUsersTable(); $this->_conn->delete('DELETE FROM users'); $this->_conn->addColumn('users', 'girlfriend', 'string'); $this->_conn->insert("INSERT INTO users (girlfriend) VALUES ('bobette')"); $this->_conn->renameColumn('users', 'girlfriend', 'exgirlfriend'); $bob = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('bobette', $bob->exgirlfriend); } public function testDistinct() { $result = $this->_conn->distinct('test'); $this->assertEquals('DISTINCT test', $result); } public function testAddOrderByForAssocLimiting() { $result = $this->_conn->addOrderByForAssocLimiting('SELECT * FROM documents ', array('order' => 'name DESC')); $this->assertEquals('SELECT * FROM documents ORDER BY name DESC', $result); } public function testModifyDate() { $modifiedDate = $this->_conn->modifyDate('start', '+', 1, 'DAY'); $this->assertEquals('datetime(start, \'+1 days\')', $modifiedDate); $t = $this->_conn->createTable('dates'); $t->column('start', 'datetime'); $t->column('end', 'datetime'); $t->end(); $this->_conn->insert( 'INSERT INTO dates (start, end) VALUES (?, ?)', array( '2011-12-10 00:00:00', '2011-12-11 00:00:00' ) ); $this->assertEquals( 1, $this->_conn->selectValue('SELECT COUNT(*) FROM dates WHERE ' . $modifiedDate . ' = end') ); $this->assertEquals( 'datetime(start, \'+2 seconds\')', $this->_conn->modifyDate('start', '+', 2, 'SECOND')); $this->assertEquals( 'datetime(start, \'+3 minutes\')', $this->_conn->modifyDate('start', '+', 3, 'MINUTE')); $this->assertEquals( 'datetime(start, \'+4 hours\')', $this->_conn->modifyDate('start', '+', 4, 'HOUR')); $this->assertEquals( 'datetime(start, \'-2 months\')', $this->_conn->modifyDate('start', '-', 2, 'MONTH')); $this->assertEquals( 'datetime(start, \'-3 years\')', $this->_conn->modifyDate('start', '-', 3, 'YEAR')); } public function testBuildClause() { $this->assertEquals( 'bitmap & 2', $this->_conn->buildClause('bitmap', '&', 2)); $this->assertEquals( array('bitmap & ?', array(2)), $this->_conn->buildClause('bitmap', '&', 2, true)); $this->assertEquals( 'bitmap | 2', $this->_conn->buildClause('bitmap', '|', 2)); $this->assertEquals( array('bitmap | ?', array(2)), $this->_conn->buildClause('bitmap', '|', 2, true)); $this->assertEquals( "LOWER(name) LIKE LOWER('%search%')", $this->_conn->buildClause('name', 'LIKE', "search")); $this->assertEquals( array("LOWER(name) LIKE LOWER(?)", array('%search%')), $this->_conn->buildClause('name', 'LIKE', "search", true)); $this->assertEquals( "LOWER(name) LIKE LOWER('%search\&replace\?%')", $this->_conn->buildClause('name', 'LIKE', "search&replace?")); $this->assertEquals( array("LOWER(name) LIKE LOWER(?)", array('%search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true)); $this->assertEquals( "(LOWER(name) LIKE LOWER('search\&replace\?%') OR LOWER(name) LIKE LOWER('% search\&replace\?%'))", $this->_conn->buildClause('name', 'LIKE', "search&replace?", false, array('begin' => true))); $this->assertEquals( array("(LOWER(name) LIKE LOWER(?) OR LOWER(name) LIKE LOWER(?))", array('search&replace?%', '% search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true, array('begin' => true))); $this->assertEquals( 'value = 2', $this->_conn->buildClause('value', '=', 2)); $this->assertEquals( array('value = ?', array(2)), $this->_conn->buildClause('value', '=', 2, true)); $this->assertEquals( "value = 'foo'", $this->_conn->buildClause('value', '=', 'foo')); $this->assertEquals( array('value = ?', array('foo')), $this->_conn->buildClause('value', '=', 'foo', true)); $this->assertEquals( "value = 'foo\?bar'", $this->_conn->buildClause('value', '=', 'foo?bar')); $this->assertEquals( array('value = ?', array('foo?bar')), $this->_conn->buildClause('value', '=', 'foo?bar', true)); } public function testInsertAndReadInCp1257() { list($conn,) = self::getConnection(array('charset' => 'cp1257')); $table = $conn->createTable('charset_cp1257'); $table->column('text', 'string'); $table->end(); $input = file_get_contents(__DIR__ . '/../../fixtures/charsets/cp1257.txt'); $conn->insert("INSERT INTO charset_cp1257 (text) VALUES (?)", array($input)); $output = $conn->selectValue('SELECT text FROM charset_cp1257'); $this->assertEquals($input, $output); } public function testInsertAndReadInUtf8() { list($conn,) = self::getConnection(array('charset' => 'utf8')); $table = $conn->createTable('charset_utf8'); $table->column('text', 'string'); $table->end(); $input = file_get_contents(__DIR__ . '/../../fixtures/charsets/utf8.txt'); $conn->insert("INSERT INTO charset_utf8 (text) VALUES (?)", array($input)); $output = $conn->selectValue('SELECT text FROM charset_utf8'); $this->assertEquals($input, $output); } /*########################################################################## # Table cache ##########################################################################*/ public function testCachedTableIndexes() { // remove any current cache. $this->_cache->set('tables/indexes/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/indexes/cache_table', 0)); $this->_createTestTable('cache_table'); $idxs = $this->_conn->indexes('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/indexes/cache_table', 0)); } public function testCachedTableColumns() { // remove any current cache. $this->_cache->set('tables/columns/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/columns/cache_table', 0)); $this->_createTestTable('cache_table'); $cols = $this->_conn->columns('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/columns/cache_table', 0)); } /*########################################################################## # Protected ##########################################################################*/ /** * Create table to perform tests on */ protected function _createTestTable($name, $options=array()) { $table = $this->_conn->createTable($name, $options); $table->column('name', 'string'); $table->column('is_college', 'boolean'); $table->end(); try { // make sure table was created $sql = "INSERT INTO $name VALUES (1, 'mlb', 0)"; $this->_conn->insert($sql); } catch (Exception $e) {} } protected function _createTestUsersTable() { $table = $this->_conn->createTable('users'); $table->column('company_id', 'integer', array('limit' => 11)); $table->column('name', 'string', array('limit' => 255, 'default' => '')); $table->column('first_name', 'string', array('limit' => 40, 'default' => '')); $table->column('approved', 'boolean', array('default' => true)); $table->column('type', 'string', array('limit' => 255, 'default' => '')); $table->column('created_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('created_on', 'date', array('default' => '0000-00-00')); $table->column('updated_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('updated_on', 'date', array('default' => '0000-00-00')); $table->end(); } protected function _columnNames($tableName) { $columns = array(); foreach ($this->_conn->columns($tableName) as $c) { $columns[] = $c->getName(); } return $columns; } /** * Get a column by name */ protected function _getColumn($table, $column) { foreach ($this->_conn->columns($table) as $col) { if ($col->getName() == $column) return $col; } } /** * Get an index by columns */ protected function _getIndex($table, $indexes) { $indexes = (array) $indexes; sort($indexes); foreach ($this->_conn->indexes($table) as $index) { $columns = $index->columns; sort($columns); if ($columns == $indexes) return $index; } } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Postgresql/ColumnDefinitionTest.php0000664000175000017500000000714712205351473024211 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ require_once __DIR__ . '/../Pdo/PgsqlBase.php'; /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Postgresql_ColumnDefinitionTest extends Horde_Db_Adapter_Pdo_PgsqlBase { protected function setUp() { parent::setUp(); list($this->_conn,) = self::getConnection(); } public function testConstruct() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $this->assertEquals('col_name', $col->getName()); $this->assertEquals('string', $col->getType()); } public function testToSql() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $this->assertEquals('"col_name" character varying(255)', $col->toSql()); } public function testToSqlLimit() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string', 40 ); $this->assertEquals('"col_name" character varying(40)', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $col->setLimit(40); $this->assertEquals('"col_name" character varying(40)', $col->toSql()); } public function testToSqlPrecisionScale() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'decimal', null, 5, 2 ); $this->assertEquals('"col_name" decimal(5, 2)', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'decimal' ); $col->setPrecision(5); $col->setScale(2); $this->assertEquals('"col_name" decimal(5, 2)', $col->toSql()); } public function testToSqlNotNull() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string', null, null, null, null, null, false ); $this->assertEquals('"col_name" character varying(255) NOT NULL', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $col->setNull(false); $this->assertEquals('"col_name" character varying(255) NOT NULL', $col->toSql()); } public function testToSqlDefault() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string', null, null, null, null, 'test' ); $this->assertEquals('"col_name" character varying(255) DEFAULT \'test\'', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $col->setDefault('test'); $this->assertEquals('"col_name" character varying(255) DEFAULT \'test\'', $col->toSql()); } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Postgresql/ColumnTest.php0000664000175000017500000002223212205351473022170 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Postgresql_ColumnTest extends Horde_Test_Case { /*########################################################################## # Construction ##########################################################################*/ public function testDefaultNull() { $col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)'); $this->assertEquals(true, $col->isNull()); } public function testNotNull() { $col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)', false); $this->assertEquals(false, $col->isNull()); } public function testName() { $col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)'); $this->assertEquals('name', $col->getName()); } public function testSqlType() { $col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)'); $this->assertEquals('character varying(255)', $col->getSqlType()); } public function testIsText() { $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)'); $this->assertTrue($col->isText()); $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'text'); $this->assertTrue($col->isText()); $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'int(11)'); $this->assertFalse($col->isText()); $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'float(11,1)'); $this->assertFalse($col->isText()); } public function testIsNumber() { $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)'); $this->assertFalse($col->isNumber()); $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'text'); $this->assertFalse($col->isNumber()); $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'int(11)'); $this->assertTrue($col->isNumber()); $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'float(11,1)'); $this->assertTrue($col->isNumber()); } /*########################################################################## # Types ##########################################################################*/ public function testTypeInteger() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'int(11)'); $this->assertEquals('integer', $col->getType()); } public function testTypeFloat() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'float(11,1)'); $this->assertEquals('float', $col->getType()); } public function testTypeDecimalPrecisionNone() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'decimal(11,0)'); $this->assertEquals('integer', $col->getType()); } public function testTypeDecimal() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'decimal(11,1)'); $this->assertEquals('decimal', $col->getType()); } public function testTypeDatetime() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'datetime'); $this->assertEquals('datetime', $col->getType()); } public function testTypeTimestamp() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'CURRENT_TIMESTAMP', 'timestamp'); $this->assertEquals('timestamp', $col->getType()); } public function testTypeTime() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'time'); $this->assertEquals('time', $col->getType()); } public function testTypeDate() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'date'); $this->assertEquals('date', $col->getType()); } public function testTypeText() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'text'); $this->assertEquals('text', $col->getType()); } public function testTypeBinary() { $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'blob(255)'); $this->assertEquals('binary', $col->getType()); } public function testTypeString() { $col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)'); $this->assertEquals('string', $col->getType()); } /*########################################################################## # Extract Limit ##########################################################################*/ public function testExtractLimitInt() { $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'int(11)'); $this->assertEquals(11, $col->getLimit()); } public function testExtractLimitVarchar() { $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)'); $this->assertEquals(255, $col->getLimit()); } public function testExtractLimitDecimal() { $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'decimal(11,1)'); $this->assertEquals('11', $col->getLimit()); } public function testExtractLimitText() { $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'text'); $this->assertEquals(null, $col->getLimit()); } public function testExtractLimitNone() { $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL'); $this->assertEquals(null, $col->getLimit()); } /*########################################################################## # Extract Precision/Scale ##########################################################################*/ public function testExtractPrecisionScale() { $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'decimal(12,1)'); $this->assertEquals('12', $col->precision()); $this->assertEquals('1', $col->scale()); } /*########################################################################## # Type Cast Values ##########################################################################*/ public function testTypeCastInteger() { $col = new Horde_Db_Adapter_Postgresql_Column('name', '1', 'int(11)', false); $this->assertEquals(1, $col->getDefault()); } public function testTypeCastFloat() { $col = new Horde_Db_Adapter_Postgresql_Column('version', '1.0', 'float(11,1)', false); $this->assertEquals(1.0, $col->getDefault()); } public function testTypeCastString() { $col = new Horde_Db_Adapter_Postgresql_Column('name', "'n/a'::character varying", 'character varying(255)', false); $this->assertEquals('n/a', $col->getDefault()); } public function testTypeCastBooleanFalse() { $col = new Horde_Db_Adapter_Postgresql_Column('is_active', '0', 'boolean', false); $this->assertSame(false, $col->getDefault()); } public function testTypeCastBooleanTrue() { $col = new Horde_Db_Adapter_Postgresql_Column('is_active', '1', 'boolean', false); $this->assertSame(true, $col->getDefault()); } /*########################################################################## # Column Types ##########################################################################*/ /*@TODO tests for PostgreSQL-specific column types */ /*########################################################################## # Defaults ##########################################################################*/ public function testDefaultDatetime() { $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'datetime'); $this->assertEquals(null, $col->getDefault()); } public function testDefaultInteger() { $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'int(11)'); $this->assertEquals(null, $col->getDefault()); } public function testDefaultString() { $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'character varying(255)'); $this->assertEquals('', $col->getDefault()); } public function testDefaultText() { $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'text'); $this->assertEquals('', $col->getDefault()); } public function testDefaultBinary() { $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'blob(255)'); $this->assertEquals('', $col->getDefault()); } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Postgresql/TableDefinitionTest.php0000664000175000017500000000342312205351473023774 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ require_once __DIR__ . '/../Pdo/PgsqlBase.php'; /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Postgresql_TableDefinitionTest extends Horde_Db_Adapter_Pdo_PgsqlBase { protected function setUp() { parent::setUp(); list($this->_conn,) = self::getConnection(); } /*########################################################################## # Public methods ##########################################################################*/ public function testConstruct() { } public function testName() { } public function testGetOptions() { } public function testPrimaryKey() { } public function testColumn() { } public function testToSql() { } /*########################################################################## # Array Access ##########################################################################*/ public function testOffsetExists() { } public function testOffsetGet() { } public function testOffsetSet() { } public function testOffsetUnset() { } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Sqlite/ColumnDefinitionTest.php0000664000175000017500000000763612205351473023312 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ require_once __DIR__ . '/../Pdo/SqliteBase.php'; /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Sqlite_ColumnDefinitionTest extends Horde_Db_Adapter_Pdo_SqliteBase { protected function setUp() { parent::setUp(); list($this->_conn,) = self::getConnection(); } protected function tearDown() { // close connection $this->_conn->disconnect(); } public function testConstruct() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $this->assertEquals('col_name', $col->getName()); $this->assertEquals('string', $col->getType()); } public function testToSql() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $this->assertEquals('"col_name" varchar(255)', $col->toSql()); } public function testToSqlLimit() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string', 40 ); $this->assertEquals('"col_name" varchar(40)', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $col->setLimit(40); $this->assertEquals('"col_name" varchar(40)', $col->toSql()); } public function testToSqlPrecisionScale() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'decimal', null, 5, 2 ); $this->assertEquals('"col_name" decimal(5, 2)', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'decimal' ); $col->setPrecision(5); $col->setScale(2); $this->assertEquals('"col_name" decimal(5, 2)', $col->toSql()); } public function testToSqlNotNull() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string', null, null, null, null, null, false ); $this->assertEquals('"col_name" varchar(255) NOT NULL', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $col->setNull(false); $this->assertEquals('"col_name" varchar(255) NOT NULL', $col->toSql()); // set attribute to the default (true) $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $col->setNull(true); $this->assertEquals('"col_name" varchar(255)', $col->toSql()); } public function testToSqlDefault() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string', null, null, null, null, 'test', null ); $this->assertEquals('"col_name" varchar(255) DEFAULT \'test\'', $col->toSql()); // set attribute instead $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->_conn, 'col_name', 'string' ); $col->setDefault('test'); $this->assertEquals('"col_name" varchar(255) DEFAULT \'test\'', $col->toSql()); } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Sqlite/ColumnTest.php0000664000175000017500000002120612205351473021266 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Sqlite_ColumnTest extends Horde_Test_Case { /*########################################################################## # Construction ##########################################################################*/ public function testDefaultNull() { $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)'); $this->assertEquals(true, $col->isNull()); } public function testNotNull() { $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)', false); $this->assertEquals(false, $col->isNull()); } public function testName() { $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)'); $this->assertEquals('name', $col->getName()); } public function testSqlType() { $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)'); $this->assertEquals('varchar(255)', $col->getSqlType()); } public function testIsText() { $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'varchar(255)'); $this->assertTrue($col->isText()); $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'text'); $this->assertTrue($col->isText()); $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'int(11)'); $this->assertFalse($col->isText()); $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'float(11,1)'); $this->assertFalse($col->isText()); } public function testIsNumber() { $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'varchar(255)'); $this->assertFalse($col->isNumber()); $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'text'); $this->assertFalse($col->isNumber()); $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'int(11)'); $this->assertTrue($col->isNumber()); $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'float(11,1)'); $this->assertTrue($col->isNumber()); } /*########################################################################## # Types ##########################################################################*/ public function testTypeInteger() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'int(11)'); $this->assertEquals('integer', $col->getType()); } public function testTypeFloat() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'float(11,1)'); $this->assertEquals('float', $col->getType()); } public function testTypeDecimalPrecisionNone() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'decimal(11,0)'); $this->assertEquals('integer', $col->getType()); } public function testTypeDecimal() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'decimal(11,1)'); $this->assertEquals('decimal', $col->getType()); } public function testTypeDatetime() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'datetime'); $this->assertEquals('datetime', $col->getType()); } public function testTypeTimestamp() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'CURRENT_TIMESTAMP', 'timestamp'); $this->assertEquals('timestamp', $col->getType()); } public function testTypeTime() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'time'); $this->assertEquals('time', $col->getType()); } public function testTypeDate() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'date'); $this->assertEquals('date', $col->getType()); } public function testTypeText() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'text'); $this->assertEquals('text', $col->getType()); } public function testTypeBinary() { $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'blob(255)'); $this->assertEquals('binary', $col->getType()); } public function testTypeString() { $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)'); $this->assertEquals('string', $col->getType()); } /*########################################################################## # Extract Limit ##########################################################################*/ public function testExtractLimitInt() { $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'int(11)'); $this->assertEquals(11, $col->getLimit()); } public function testExtractLimitVarchar() { $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'varchar(255)'); $this->assertEquals(255, $col->getLimit()); } public function testExtractLimitDecimal() { $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'decimal(11,1)'); $this->assertEquals('11', $col->getLimit()); } public function testExtractLimitText() { $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'text'); $this->assertEquals(null, $col->getLimit()); } public function testExtractLimitNone() { $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL'); $this->assertEquals(null, $col->getLimit()); } /*########################################################################## # Extract Precision/Scale ##########################################################################*/ public function testExtractPrecisionScale() { $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'decimal(12,1)'); $this->assertEquals('12', $col->precision()); $this->assertEquals('1', $col->scale()); } /*########################################################################## # Type Cast Values ##########################################################################*/ public function testTypeCastInteger() { $col = new Horde_Db_Adapter_Sqlite_Column('name', '1', 'int(11)', false); $this->assertEquals(1, $col->getDefault()); } public function testTypeCastFloat() { $col = new Horde_Db_Adapter_Sqlite_Column('version', '1.0', 'float(11,1)', false); $this->assertEquals(1.0, $col->getDefault()); } public function testTypeCastString() { $col = new Horde_Db_Adapter_Sqlite_Column('name', 'n/a', 'varchar(255)', false); $this->assertEquals('n/a', $col->getDefault()); } public function testTypeCastBooleanFalse() { $col = new Horde_Db_Adapter_Sqlite_Column('is_active', 'f', 'boolean', false); $this->assertSame(false, $col->getDefault()); } public function testTypeCastBooleanTrue() { $col = new Horde_Db_Adapter_Sqlite_Column('is_active', 't', 'boolean', false); $this->assertSame(true, $col->getDefault()); } /*########################################################################## # Defaults ##########################################################################*/ public function testDefaultDatetime() { $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'datetime'); $this->assertEquals(null, $col->getDefault()); } public function testDefaultInteger() { $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'int(11)'); $this->assertEquals(null, $col->getDefault()); } public function testDefaultString() { $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'varchar(255)'); $this->assertEquals('', $col->getDefault()); } public function testDefaultText() { $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'text'); $this->assertEquals('', $col->getDefault()); } public function testDefaultBinary() { $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'blob(255)'); $this->assertEquals('', $col->getDefault()); } } Horde_Db-2.0.4/test/Horde/Db/Adapter/Sqlite/TableDefinitionTest.php0000664000175000017500000000360012205351473023067 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ require_once __DIR__ . '/../Pdo/SqliteBase.php'; /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Sqlite_TableDefinitionTest extends Horde_Db_Adapter_Pdo_SqliteBase { protected function setUp() { parent::setUp(); list($this->_conn,) = self::getConnection(); } protected function tearDown() { // close connection $this->_conn->disconnect(); } /*########################################################################## # Public methods ##########################################################################*/ public function testConstruct() { } public function testName() { } public function testGetOptions() { } public function testPrimaryKey() { } public function testColumn() { } public function testToSql() { } /*########################################################################## # Array Access ##########################################################################*/ public function testOffsetExists() { } public function testOffsetGet() { } public function testOffsetSet() { } public function testOffsetUnset() { } } Horde_Db-2.0.4/test/Horde/Db/Adapter/conf.php.dist0000644000175000017500000000210312205351473017610 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_MysqliTest extends Horde_Test_Case { protected static $columnTest; protected static $tableTest; protected static $skip = true; protected static $reason = 'The MySQLi adapter is not available'; public static function setUpBeforeClass() { if (extension_loaded('mysqli')) { self::$skip = false; list($conn,) = self::getConnection(); if (self::$skip) { return; } $conn->disconnect(); } require_once __DIR__ . '/Mysql/ColumnDefinition.php'; require_once __DIR__ . '/Mysql/TableDefinition.php'; self::$columnTest = new Horde_Db_Adapter_Mysql_ColumnDefinition(); self::$tableTest = new Horde_Db_Adapter_Mysql_TableDefinition(); } public static function getConnection($overrides = array()) { $config = Horde_Test_Case::getConfig('DB_ADAPTER_MYSQLI_TEST_CONFIG', null, array('host' => 'localhost', 'username' => '', 'password' => '', 'dbname' => 'test')); if (isset($config['db']['adapter']['mysqli']['test'])) { $config = $config['db']['adapter']['mysqli']['test']; } if (!is_array($config)) { self::$skip = true; self::$reason = 'No configuration for mysqli test'; return; } $config = array_merge($config, $overrides); $conn = new Horde_Db_Adapter_Mysqli($config); $cache = new Horde_Cache(new Horde_Cache_Storage_Mock()); $conn->setCache($cache); return array($conn, $cache); } protected function setUp() { if (self::$skip) { $this->markTestSkipped(self::$reason); } list($this->_conn, $this->_cache) = self::getConnection(); self::$columnTest->conn = $this->_conn; self::$tableTest->conn = $this->_conn; // clear out detritus from any previous test runs. $this->_dropTestTables(); $table = $this->_conn->createTable('unit_tests'); $table->column('integer_value', 'integer', array('limit' => 11, 'default' => 0)); $table->column('string_value', 'string', array('limit' => 255, 'default' => '')); $table->column('text_value', 'text', array('null' => false, 'default' => '')); $table->column('float_value', 'float', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('decimal_value', 'decimal', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('datetime_value', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('date_value', 'date', array('default' => '0000-00-00')); $table->column('time_value', 'time', array('default' => '00:00:00')); $table->column('blob_value', 'binary', array('null' => false, 'default' => '')); $table->column('boolean_value', 'boolean', array('default' => false)); $table->column('email_value', 'string', array('limit' => 255, 'default' => '')); $table->end(); $this->_conn->addIndex('unit_tests', 'string_value', array('name' => 'string_value')); $this->_conn->addIndex('unit_tests', 'integer_value', array('name' => 'integer_value', 'unique' => true)); $this->_conn->addIndex('unit_tests', array('integer_value', 'string_value'), array('name' => 'integer_string')); // read sql file for statements $statements = array(); $current_stmt = ''; $fp = fopen(__DIR__ . '/../fixtures/unit_tests.sql', 'r'); while ($line = fgets($fp, 8192)) { $line = rtrim(preg_replace('/^(.*)--.*$/s', '\1', $line)); if (!$line) { continue; } $current_stmt .= $line; if (substr($line, -1) == ';') { // leave off the ending ; $statements[] = substr($current_stmt, 0, -1); $current_stmt = ''; } } // run statements foreach ($statements as $stmt) { $this->_conn->execute($stmt); } } protected function tearDown() { // clean up $this->_dropTestTables(); // close connection $this->_conn->disconnect(); } /*########################################################################## # Connection ##########################################################################*/ public function testConnect() { $this->assertTrue($this->_conn->isActive()); } public function testDisconnect() { $this->_conn->disconnect(); $this->assertFalse($this->_conn->isActive()); $this->_conn->connect(); $this->assertTrue($this->_conn->isActive()); } public function testReconnect() { $this->_conn->reconnect(); $this->assertTrue($this->_conn->isActive()); } /*########################################################################## # Accessor ##########################################################################*/ public function testAdapterName() { $this->assertEquals('MySQLi', $this->_conn->adapterName()); } public function testSupportsMigrations() { $this->assertTrue($this->_conn->supportsMigrations()); } public function testSupportsCountDistinct() { $this->assertTrue($this->_conn->supportsCountDistinct()); } public function testGetCharset() { $this->assertEquals('utf8', strtolower($this->_conn->getCharset())); } /*########################################################################## # Database Statements ##########################################################################*/ public function testExecute() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->execute($sql); $row = $result->fetch_assoc(); $this->assertEquals(1, $row['id']); } public function testSelect() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->select($sql); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParameters() { $sql = "SELECT * FROM unit_tests WHERE id=?"; $result = $this->_conn->select($sql, array(1)); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParametersQuotesString() { $sql = "SELECT * FROM unit_tests WHERE string_value=?"; $result = $this->_conn->select($sql, array('name a')); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectAll() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectAll($sql); $this->assertInternalType('array', $result); $this->assertGreaterThan(0, count($result)); $this->assertEquals(1, $result[0]['id']); } public function testSelectOne() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectOne($sql); $this->assertEquals(1, $result['id']); } public function testSelectValue() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectValue($sql); $this->assertEquals(1, $result); } public function testSelectValues() { $sql = "SELECT * FROM unit_tests"; $result = $this->_conn->selectValues($sql); $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result); } public function testInsert() { $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $result = $this->_conn->insert($sql); $this->assertEquals(7, $result); } public function testUpdate() { $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)"; $result = $this->_conn->update($sql); $this->assertEquals(1, $result); } public function testDelete() { $sql = "DELETE FROM unit_tests WHERE id IN (1,2)"; $result = $this->_conn->delete($sql); $this->assertEquals(2, $result); } public function testTransactionStarted() { $this->assertFalse($this->_conn->transactionStarted()); $this->_conn->beginDbTransaction(); $this->assertTrue($this->_conn->transactionStarted()); $this->_conn->commitDbTransaction(); $this->assertFalse($this->_conn->transactionStarted()); } public function testTransactionCommit() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql); $this->_conn->commitDbTransaction(); // make sure it inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals('999', $this->_conn->selectValue($sql)); // query without transaction and with new connection (see bug #10578). $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (8, 1000)"; $this->_conn->insert($sql); // make sure it inserted $this->_conn->reconnect(); $sql = "SELECT integer_value FROM unit_tests WHERE id='8'"; $this->assertEquals('1000', $this->_conn->selectValue($sql)); } public function testTransactionRollback() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql); $this->_conn->rollbackDbTransaction(); // make sure it inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals(null, $this->_conn->selectValue($sql)); // query without transaction and with new connection (see bug #10578). $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql); // make sure it inserted $this->_conn->reconnect(); $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals(999, $this->_conn->selectValue($sql)); } /*########################################################################## # Quoting ##########################################################################*/ public function testQuoteNull() { $this->assertEquals('NULL', $this->_conn->quote(null)); } public function testQuoteTrue() { $this->assertEquals('1', $this->_conn->quote(true)); } public function testQuoteFalse() { $this->assertEquals('0', $this->_conn->quote(false)); } public function testQuoteInteger() { $this->assertEquals('42', $this->_conn->quote(42)); } public function testQuoteFloat() { $this->assertEquals('42.2', $this->_conn->quote(42.2)); setlocale(LC_NUMERIC, 'de_DE.UTF-8'); $this->assertEquals('42.2', $this->_conn->quote(42.2)); } public function testQuoteString() { $this->assertEquals("'my string'", $this->_conn->quote('my string')); } public function testQuoteDirtyString() { $this->assertEquals("'derek\'s string'", $this->_conn->quote('derek\'s string')); } public function testQuoteColumnName() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)'); $this->assertEquals('1', $this->_conn->quote(true, $col)); } public function testQuoteBinary() { // Test string is foo\0ba'r - should be 8 bytes long $original = base64_decode('Zm9vAGJhJ3I='); $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); $this->_conn->insert('INSERT INTO binary_testings (data) VALUES (?)', array(new Horde_Db_Value_Binary($original))); $retrieved = $this->_conn->selectValue('SELECT data FROM binary_testings'); $columns = $this->_conn->columns('binary_testings'); $retrieved = $columns['data']->binaryToString($retrieved); $this->assertEquals($original, $retrieved); } /*########################################################################## # Schema Statements ##########################################################################*/ /** * We specifically do a manual INSERT here, and then test only the SELECT * functionality. This allows us to more easily catch INSERT being broken, * but SELECT actually working fine. */ public function testNativeDecimalInsertManualVsAutomatic() { $this->_createTestUsersTable(); $correctValue = 12345678901234567890.0123456789; $this->_conn->addColumn("users", "wealth", 'decimal', array('precision' => 30, 'scale' => 10)); // do a manual insertion $this->_conn->execute("INSERT INTO users (wealth) VALUES ('12345678901234567890.0123456789')"); // SELECT @todo - type cast attribute values $user = (object)$this->_conn->selectOne('SELECT * FROM users'); // assert_kind_of BigDecimal, row.wealth // If this assert fails, that means the SELECT is broken! $this->assertEquals($correctValue, $user->wealth); // Reset to old state $this->_conn->delete('DELETE FROM users'); // Now use the Adapter insertion $this->_conn->insert('INSERT INTO users (wealth) VALUES (12345678901234567890.0123456789)'); // SELECT @todo - type cast attribute values $user = (object)$this->_conn->selectOne('SELECT * FROM users'); // assert_kind_of BigDecimal, row.wealth // If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken! $this->assertEquals($correctValue, $user->wealth); } public function testNativeTypes() { $this->_createTestUsersTable(); $this->_conn->addColumn("users", "last_name", 'string'); $this->_conn->addColumn("users", "bio", 'text'); $this->_conn->addColumn("users", "age", 'integer'); $this->_conn->addColumn("users", "height", 'float'); $this->_conn->addColumn("users", "wealth", 'decimal', array('precision' => '30', 'scale' => '10')); $this->_conn->addColumn("users", "birthday", 'datetime'); $this->_conn->addColumn("users", "favorite_day", 'date'); $this->_conn->addColumn("users", "moment_of_truth", 'datetime'); $this->_conn->addColumn("users", "male", 'boolean'); $this->_conn->insert('INSERT INTO users (first_name, last_name, bio, age, height, wealth, birthday, favorite_day, moment_of_truth, male, company_id) ' . "VALUES ('bob', 'bobsen', 'I was born ....', 18, 1.78, 12345678901234567890.0123456789, '2005-01-01 12:23:40', '1980-03-05', '1582-10-10 21:40:18', 1, 1)"); $bob = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('bob', $bob->first_name); $this->assertEquals('bobsen', $bob->last_name); $this->assertEquals('I was born ....', $bob->bio); $this->assertEquals(18, $bob->age); // Test for 30 significent digits (beyond the 16 of float), 10 of them // after the decimal place. $this->assertEquals('12345678901234567890.0123456789', $bob->wealth); $this->assertEquals(1, $bob->male); // @todo - type casting } public function testNativeDatabaseTypes() { $types = $this->_conn->nativeDatabaseTypes(); $this->assertEquals(array('name' => 'int', 'limit' => 11), $types['integer']); } public function testUnabstractedDatabaseDependentTypes() { $this->_createTestUsersTable(); $this->_conn->delete('DELETE FROM users'); $this->_conn->addColumn('users', 'intelligence_quotient', 'tinyint'); $this->_conn->insert('INSERT INTO users (intelligence_quotient) VALUES (300)'); $jonnyg = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('127', $jonnyg->intelligence_quotient); } public function testTableAliasLength() { $len = $this->_conn->tableAliasLength(); $this->assertEquals(255, $len); } public function testTableAliasFor() { $alias = $this->_conn->tableAliasFor('my_table_name'); $this->assertEquals('my_table_name', $alias); } public function testTables() { $tables = $this->_conn->tables(); $this->assertTrue(count($tables) > 0); $this->assertContains('unit_tests', $tables); } public function testPrimaryKey() { $pk = $this->_conn->primaryKey('unit_tests'); $this->assertEquals('id', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->assertEquals('id', $pk->columns[0]); $table = $this->_conn->createTable('pk_tests', array('autoincrementKey' => false)); $table->column('foo', 'string'); $table->column('bar', 'string'); $table->end(); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', 'foo'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->_conn->removePrimaryKey('pk_tests'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', array('foo', 'bar')); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo,bar', (string)$pk); } public function testIndexes() { $indexes = $this->_conn->indexes('unit_tests'); $this->assertEquals(3, count($indexes)); // unique index $col = array('integer_value'); $this->assertEquals('unit_tests', $indexes[0]->table); $this->assertEquals('integer_value', $indexes[0]->name); $this->assertEquals(true, $indexes[0]->unique); $this->assertEquals($col, $indexes[0]->columns); // normal index $col = array('string_value'); $this->assertEquals('unit_tests', $indexes[1]->table); $this->assertEquals('string_value', $indexes[1]->name); $this->assertEquals(false, $indexes[1]->unique); $this->assertEquals($col, $indexes[1]->columns); // multi-column index $col = array('integer_value', 'string_value'); $this->assertEquals('unit_tests', $indexes[2]->table); $this->assertEquals('integer_string', $indexes[2]->name); $this->assertEquals(false, $indexes[2]->unique); $this->assertEquals($col, $indexes[2]->columns); } public function testColumns() { $columns = $this->_conn->columns('unit_tests'); $this->assertEquals(12, count($columns)); $col = $columns['id']; $this->assertEquals('id', $col->getName()); $this->assertEquals('integer', $col->getType()); $this->assertEquals(false, $col->isNull()); $this->assertEquals(10, $col->getLimit()); $this->assertEquals(true, $col->isUnsigned()); $this->assertEquals('', $col->getDefault()); $this->assertEquals('int(10) unsigned', $col->getSqlType()); $this->assertEquals(false, $col->isText()); $this->assertEquals(true, $col->isNumber()); } public function testCreateTable() { $this->_createTestTable('sports'); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableNoPk() { $this->_createTestTable('sports', array('autoincrementKey' => false)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertNull($this->_conn->selectValue($sql)); } catch (Exception $e) { return; } $this->fail("Expected exception for no pk"); } public function testCreateTableWithNamedPk() { $this->_createTestTable('sports', array('autoincrementKey' => 'sports_id')); $sql = "SELECT sports_id FROM sports WHERE sports_id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertNull($this->_conn->selectValue($sql)); } catch (Exception $e) { return; } $this->fail("Expected exception for wrong pk name"); } public function testCreateTableWithSeparatePk() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'autoincrementKey'); $pkColumn = $table['foo']; $this->assertEquals('`foo` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', $pkColumn->toSql()); } public function testCreateTableCompositePk() { $table = $this->_conn->createTable('testings', array('autoincrementKey' => array('a_id', 'b_id'))); $table->column('a_id', 'integer'); $table->column('b_id', 'integer'); $table->end(); $pk = $this->_conn->primaryKey('testings'); $this->assertEquals(array('a_id', 'b_id'), $pk->columns); } public function testCreateTableForce() { $this->_createTestTable('sports'); $this->_createTestTable('sports', array('force' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableTemporary() { $this->_createTestTable('sports', array('temporary' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableAddsId() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[] = $col->getName(); } sort($columns); $this->assertEquals(array('foo', 'id'), $columns); } public function testCreateTableWithNotNullColumn() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('null' => false)); $table->end(); try { $this->_conn->execute("INSERT INTO testings (foo) VALUES (NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testCreateTableWithDefaults() { $table = $this->_conn->createTable('testings'); $table->column('one', 'string', array('default' => 'hello')); $table->column('two', 'boolean', array('default' => true)); $table->column('three', 'boolean', array('default' => false)); $table->column('four', 'integer', array('default' => 1)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals('hello', $columns['one']->getDefault()); $this->assertTrue($columns['two']->getDefault()); $this->assertFalse($columns['three']->getDefault()); $this->assertEquals(1, $columns['four']->getDefault()); } public function testCreateTableWithLimits() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('limit' => 80)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals(80, $columns['foo']->getLimit()); } public function testCreateTableWithBinaryColumn() { try { $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); } catch (Exception $e) { $this->fail('Unexepected exception raised'); } $columns = $this->_conn->columns('binary_testings'); foreach ($columns as $c) { if ($c->getName() == 'data') { $dataColumn = $c; } } $this->assertEquals('', $dataColumn->getDefault()); } public function testRenameTable() { // Simple rename then select test $this->_createTestTable('sports'); $this->_conn->renameTable('sports', 'my_sports'); $sql = "SELECT id FROM my_sports WHERE id = 1"; $this->assertEquals("1", $this->_conn->selectValue($sql)); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); // Rename then insert test $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->execute($sql); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM octopuses WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testRenameTableWithAnIndex() { $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->addIndex('octopuses', 'url'); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->execute($sql); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); $indexes = $this->_conn->indexes('octopi'); $this->assertEquals('url', $indexes[0]->columns[0]); } public function testDropTable() { $this->_createTestTable('sports'); $this->_conn->dropTable('sports'); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testAddColumn() { $this->_createTestTable('sports'); $this->_conn->addColumn('sports', 'modified_at', 'date'); $this->_conn->update("UPDATE sports SET modified_at = '2007-01-01'"); $sql = "SELECT modified_at FROM sports WHERE id = 1"; $this->assertEquals("2007-01-01", $this->_conn->selectValue($sql)); $this->_conn->addColumn('sports', 'with_default', 'integer', array('default' => 1)); $sql = "SELECT with_default FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testRemoveColumn() { $this->_createTestTable('sports'); $sql = "SELECT name FROM sports WHERE id = 1"; $this->assertEquals("mlb", $this->_conn->selectValue($sql)); $this->_conn->removeColumn('sports', 'name'); try { $sql = "SELECT name FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Column exists where it shouldn't have"); } public function testChangeColumn() { $this->_createTestUsersTable(); $this->_conn->addColumn('users', 'age', 'integer'); $oldColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertTrue($found); $this->_conn->changeColumn('users', 'age', 'string'); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'string') { $found = true; } } $this->assertTrue($found); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => false)); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == false) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => true)); } public function testChangeColumnDefault() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'name'); $this->assertEquals('', $beforeChange->getDefault()); $this->_conn->changeColumnDefault('sports', 'name', 'test'); $afterChange = $this->_getColumn('sports', 'name'); $this->assertEquals('test', $afterChange->getDefault()); } public function testChangeColumnType() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string'); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar(255)', $afterChange->getSqlType()); $table = $this->_conn->createTable('text_to_binary'); $table->column('data', 'text'); $table->end(); $this->_conn->insert('INSERT INTO text_to_binary (data) VALUES (?)', array("foo\0bar")); $this->_conn->changeColumn('text_to_binary', 'data', 'binary'); $afterChange = $this->_getColumn('text_to_binary', 'data'); $this->assertEquals('longblob', $afterChange->getSqlType()); $this->assertEquals( "foo\0bar", $this->_conn->selectValue('SELECT data FROM text_to_binary')); } public function testChangeColumnLimit() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string', array('limit' => '40')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar(40)', $afterChange->getSqlType()); } public function testChangeColumnPrecisionScale() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'decimal', array('precision' => '5', 'scale' => '2')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('decimal(5,2)', $afterChange->getSqlType()); } public function testChangeColumnUnsigned() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'integer'); $table->end(); $beforeChange = $this->_getColumn('testings', 'foo'); $this->assertFalse($beforeChange->isUnsigned()); $this->_conn->execute("INSERT INTO testings (id, foo) VALUES (1, -1)"); $this->_conn->changeColumn('testings', 'foo', 'integer', array('unsigned' => true)); $afterChange = $this->_getColumn('testings', 'foo'); $this->assertTrue($afterChange->isUnsigned()); $row = (object)$this->_conn->selectOne('SELECT * FROM testings'); $this->assertEquals(0, $row->foo); } public function testRenameColumn() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'nick_name'); $this->assertTrue(in_array('nick_name', $this->_columnNames('users'))); $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->renameColumn('sports', 'is_college', 'is_renamed'); $afterChange = $this->_getColumn('sports', 'is_renamed'); $this->assertEquals('tinyint(1)', $afterChange->getSqlType()); } public function testRenameColumnWithSqlReservedWord() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'group'); $this->assertTrue(in_array('group', $this->_columnNames('users'))); } public function testAddIndex() { $this->_createTestUsersTable(); // Limit size of last_name and key columns to support Firebird index limitations $this->_conn->addColumn('users', 'last_name', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'key', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'administrator', 'boolean'); $this->_conn->addIndex('users', 'last_name'); $this->_conn->removeIndex('users', 'last_name'); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('column' => array('last_name', 'first_name'))); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('name' => 'index_users_on_last_name_and_first_name')); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', 'last_name_and_first_name'); // quoting $this->_conn->addIndex('users', array('key'), array('name' => 'key_idx', 'unique' => true)); $this->_conn->removeIndex('users', array('name' => 'key_idx', 'unique' => true)); $this->_conn->addIndex('users', array('last_name', 'first_name', 'administrator'), array('name' => "named_admin")); $this->_conn->removeIndex('users', array('name' => 'named_admin')); } public function testAddIndexDefault() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); } public function testAddIndexMultiColumn() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); } public function testAddIndexUnique() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('unique' => true)); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertTrue($index->unique); } public function testAddIndexName() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertEquals('test', $index->name); } public function testRemoveIndexSingleColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => 'is_college')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testRemoveIndexMultiColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => array('name', 'is_college'))); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); } public function testRemoveIndexByName() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testIndexNameInvalid() { try { $name = $this->_conn->indexName('sports'); } catch (Horde_Db_Exception $e) { return; } $this->fail("Adding an index with crappy options worked where it shouldn't have"); } public function testIndexNameBySingleColumn() { $name = $this->_conn->indexName('sports', array('column' => 'is_college')); $this->assertEquals('index_sports_on_is_college', $name); } public function testIndexNameByMultiColumn() { $name = $this->_conn->indexName('sports', array('column' => array('name', 'is_college'))); $this->assertEquals('index_sports_on_name_and_is_college', $name); } public function testIndexNameByName() { $name = $this->_conn->indexName('sports', array('name' => 'test')); $this->assertEquals('test', $name); } public function testTypeToSqlTypePrimaryKey() { $result = $this->_conn->typeToSql('autoincrementKey'); $this->assertEquals('int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', $result); } public function testTypeToSqlTypeString() { $result = $this->_conn->typeToSql('string'); $this->assertEquals('varchar(255)', $result); } public function testTypeToSqlTypeText() { $result = $this->_conn->typeToSql('text'); $this->assertEquals('text', $result); } public function testTypeToSqlTypeBinary() { $result = $this->_conn->typeToSql('binary'); $this->assertEquals('longblob', $result); } public function testTypeToSqlTypeFloat() { $result = $this->_conn->typeToSql('float'); $this->assertEquals('float', $result); } public function testTypeToSqlTypeDatetime() { $result = $this->_conn->typeToSql('datetime'); $this->assertEquals('datetime', $result); } public function testTypeToSqlTypeTimestamp() { $result = $this->_conn->typeToSql('timestamp'); $this->assertEquals('datetime', $result); } public function testTypeToSqlInt() { $result = $this->_conn->typeToSql('integer'); $this->assertEquals('int(11)', $result); } public function testTypeToSqlIntUnsigned() { $result = $this->_conn->typeToSql('integer', null, null, null, true); $this->assertEquals('int(10) UNSIGNED', $result); } public function testTypeToSqlIntLimit() { $result = $this->_conn->typeToSql('integer', '1'); $this->assertEquals('int(1)', $result); } public function testTypeToSqlDecimalPrecision() { $result = $this->_conn->typeToSql('decimal', null, '5'); $this->assertEquals('decimal(5)', $result); } public function testTypeToSqlDecimalScale() { $result = $this->_conn->typeToSql('decimal', null, '5', '2'); $this->assertEquals('decimal(5, 2)', $result); } public function testTypeToSqlBoolean() { $result = $this->_conn->typeToSql('boolean'); $this->assertEquals('tinyint(1)', $result); } public function testAddColumnOptions() { $result = $this->_conn->addColumnOptions("test", array()); $this->assertEquals("test", $result); } public function testAddColumnOptionsDefault() { $options = array('default' => '0'); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test DEFAULT '0'", $result); } public function testAddColumnOptionsNull() { $options = array('null' => true); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test", $result); } public function testAddColumnOptionsNotNull() { $options = array('null' => false); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test NOT NULL", $result); } public function testAddColumnNotNullWithoutDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => '')); try { $this->_conn->execute("INSERT INTO testings (foo, bar) VALUES ('hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddColumnNotNullWithDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->execute("INSERT INTO testings (id, foo) VALUES ('1', 'hello')"); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => 'default')); try { $this->_conn->execute("INSERT INTO testings (id, foo, bar) VALUES (2, 'hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddRemoveSingleField() { $this->_createTestUsersTable(); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); $this->_conn->addColumn('users', 'last_name', 'string'); $this->assertTrue(in_array('last_name', $this->_columnNames('users'))); $this->_conn->removeColumn('users', 'last_name'); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); } public function testAddRename() { $this->_createTestUsersTable(); $this->_conn->delete('DELETE FROM users'); $this->_conn->addColumn('users', 'girlfriend', 'string'); $this->_conn->insert("INSERT INTO users (girlfriend) VALUES ('bobette')"); $this->_conn->renameColumn('users', 'girlfriend', 'exgirlfriend'); $bob = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('bobette', $bob->exgirlfriend); } public function testDistinct() { $result = $this->_conn->distinct("test"); $this->assertEquals("DISTINCT test", $result); } public function testAddOrderByForAssocLimiting() { $result = $this->_conn->addOrderByForAssocLimiting("SELECT * FROM documents ", array('order' => 'name DESC')); $this->assertEquals("SELECT * FROM documents ORDER BY name DESC", $result); } public function testInterval() { $this->assertEquals('INTERVAL 1 DAY', $this->_conn->interval('1 DAY', '')); } public function testModifyDate() { $modifiedDate = $this->_conn->modifyDate('start', '+', 1, 'DAY'); $this->assertEquals('start + INTERVAL \'1\' DAY', $modifiedDate); $t = $this->_conn->createTable('dates'); $t->column('start', 'datetime'); $t->column('end', 'datetime'); $t->end(); $this->_conn->insert( 'INSERT INTO dates (start, end) VALUES (?, ?)', array( '2011-12-10 00:00:00', '2011-12-11 00:00:00' ) ); $this->assertEquals( 1, $this->_conn->selectValue('SELECT COUNT(*) FROM dates WHERE ' . $modifiedDate . ' = end') ); } public function testBuildClause() { $this->assertEquals( 'bitmap & 2', $this->_conn->buildClause('bitmap', '&', 2)); $this->assertEquals( array('bitmap & ?', array(2)), $this->_conn->buildClause('bitmap', '&', 2, true)); $this->assertEquals( 'bitmap | 2', $this->_conn->buildClause('bitmap', '|', 2)); $this->assertEquals( array('bitmap | ?', array(2)), $this->_conn->buildClause('bitmap', '|', 2, true)); $this->assertEquals( "LOWER(name) LIKE LOWER('%search%')", $this->_conn->buildClause('name', 'LIKE', "search")); $this->assertEquals( array("LOWER(name) LIKE LOWER(?)", array('%search%')), $this->_conn->buildClause('name', 'LIKE', "search", true)); $this->assertEquals( "LOWER(name) LIKE LOWER('%search\&replace\?%')", $this->_conn->buildClause('name', 'LIKE', "search&replace?")); $this->assertEquals( array("LOWER(name) LIKE LOWER(?)", array('%search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true)); $this->assertEquals( "(LOWER(name) LIKE LOWER('search\&replace\?%') OR LOWER(name) LIKE LOWER('% search\&replace\?%'))", $this->_conn->buildClause('name', 'LIKE', "search&replace?", false, array('begin' => true))); $this->assertEquals( array("(LOWER(name) LIKE LOWER(?) OR LOWER(name) LIKE LOWER(?))", array('search&replace?%', '% search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true, array('begin' => true))); $this->assertEquals( 'value = 2', $this->_conn->buildClause('value', '=', 2)); $this->assertEquals( array('value = ?', array(2)), $this->_conn->buildClause('value', '=', 2, true)); $this->assertEquals( "value = 'foo'", $this->_conn->buildClause('value', '=', 'foo')); $this->assertEquals( array('value = ?', array('foo')), $this->_conn->buildClause('value', '=', 'foo', true)); $this->assertEquals( "value = 'foo\?bar'", $this->_conn->buildClause('value', '=', 'foo?bar')); $this->assertEquals( array('value = ?', array('foo?bar')), $this->_conn->buildClause('value', '=', 'foo?bar', true)); } public function testInsertAndReadInCp1257() { list($conn,) = self::getConnection(array('charset' => 'cp1257')); $table = $conn->createTable('charset_cp1257'); $table->column('text', 'string'); $table->end(); $input = file_get_contents(__DIR__ . '/../fixtures/charsets/cp1257.txt'); $conn->insert("INSERT INTO charset_cp1257 (text) VALUES (?)", array($input)); $output = $conn->selectValue('SELECT text FROM charset_cp1257'); $this->assertEquals($input, $output); } public function testInsertAndReadInUtf8() { list($conn,) = self::getConnection(array('charset' => 'utf8')); $table = $conn->createTable('charset_utf8'); $table->column('text', 'string'); $table->end(); $input = file_get_contents(__DIR__ . '/../fixtures/charsets/utf8.txt'); $conn->insert("INSERT INTO charset_utf8 (text) VALUES (?)", array($input)); $output = $conn->selectValue('SELECT text FROM charset_utf8'); $this->assertEquals($input, $output); } /*########################################################################## # Table cache ##########################################################################*/ public function testCachedTableIndexes() { // remove any current cache. $this->_cache->set('tables/indexes/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/indexes/cache_table')); $this->_createTestTable('cache_table'); $idxs = $this->_conn->indexes('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/indexes/cache_table')); } public function testCachedTableColumns() { // remove any current cache. $this->_cache->set('tables/columns/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/columns/cache_table')); $this->_createTestTable('cache_table'); $cols = $this->_conn->columns('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/columns/cache_table')); } /*########################################################################## # Protected ##########################################################################*/ /** * Create table to perform tests on */ protected function _createTestTable($name, $options=array()) { $table = $this->_conn->createTable($name, $options); $table->column('name', 'string'); $table->column('is_college', 'boolean'); $table->end(); try { // make sure table was created $sql = "INSERT INTO $name VALUES (1, 'mlb', 0)"; $this->_conn->insert($sql); } catch (Exception $e) {} } protected function _createTestUsersTable() { $table = $this->_conn->createTable('users'); $table->column('company_id', 'integer', array('limit' => 11)); $table->column('name', 'string', array('limit' => 255, 'default' => '')); $table->column('first_name', 'string', array('limit' => 40, 'default' => '')); $table->column('approved', 'boolean', array('default' => true)); $table->column('type', 'string', array('limit' => 255, 'default' => '')); $table->column('created_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('created_on', 'date', array('default' => '0000-00-00')); $table->column('updated_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('updated_on', 'date', array('default' => '0000-00-00')); $table->end(); } /** * drop test tables */ protected function _dropTestTables() { $tables = array( 'binary_testings', 'cache_table', 'charset_cp1257', 'charset_utf8', 'dates', 'my_sports', 'octopi', 'pk_tests', 'schema_info', 'sports', 'testings', 'text_to_binary', 'unit_tests', 'users', ); foreach ($tables as $table) { try { $this->_conn->dropTable($table); } catch (Exception $e) {} } } protected function _columnNames($tableName) { $columns = array(); foreach ($this->_conn->columns($tableName) as $c) { $columns[] = $c->getName(); } return $columns; } /** * Get a column by name */ protected function _getColumn($table, $column) { foreach ($this->_conn->columns($table) as $col) { if ($col->getName() == $column) return $col; } } /** * Get an index by columns */ protected function _getIndex($table, $indexes) { $indexes = (array) $indexes; sort($indexes); foreach ($this->_conn->indexes($table) as $index) { $columns = $index->columns; sort($columns); if ($columns == $indexes) return $index; } } public function testColumnConstruct() { self::$columnTest->testConstruct(); } public function testColumnToSql() { self::$columnTest->testToSql(); } public function testColumnToSqlLimit() { self::$columnTest->testToSqlLimit(); } public function testColumnToSqlPrecisionScale() { self::$columnTest->testToSqlPrecisionScale(); } public function testColumnToSqlUnsigned() { self::$columnTest->testToSqlUnsigned(); } public function testColumnToSqlNotNull() { self::$columnTest->testToSqlNotNull(); } public function testColumnToSqlDefault() { self::$columnTest->testToSqlDefault(); } public function testTableConstruct() { self::$tableTest->testConstruct(); } public function testTableName() { self::$tableTest->testName(); } public function testTableGetOptions() { self::$tableTest->testGetOptions(); } public function testTablePrimaryKey() { self::$tableTest->testPrimaryKey(); } public function testTableColumn() { self::$tableTest->testColumn(); } public function testTableToSql() { self::$tableTest->testToSql(); } } Horde_Db-2.0.4/test/Horde/Db/Adapter/MysqlTest.php0000664000175000017500000016020212205351473017675 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_MysqlTest extends Horde_Test_Case { protected static $columnTest; protected static $tableTest; protected static $skip = true; protected static $reason = 'The MySQL adapter is not available'; public static function setUpBeforeClass() { if (extension_loaded('mysql')) { self::$skip = false; list($conn,) = self::getConnection(); if (self::$skip) { return; } $conn->disconnect(); } require_once __DIR__ . '/Mysql/ColumnDefinition.php'; require_once __DIR__ . '/Mysql/TableDefinition.php'; self::$columnTest = new Horde_Db_Adapter_Mysql_ColumnDefinition(); self::$tableTest = new Horde_Db_Adapter_Mysql_TableDefinition(); } public static function getConnection($overrides = array()) { $config = Horde_Test_Case::getConfig('DB_ADAPTER_MYSQL_TEST_CONFIG', null, array('host' => 'localhost', 'username' => '', 'password' => '', 'dbname' => 'test')); if (isset($config['db']['adapter']['mysql']['test'])) { $config = $config['db']['adapter']['mysql']['test']; } if (!is_array($config)) { self::$skip = true; self::$reason = 'No configuration for mysql test'; return; } $config = array_merge($config, $overrides); $conn = new Horde_Db_Adapter_Mysql($config); $cache = new Horde_Cache(new Horde_Cache_Storage_Mock()); $conn->setCache($cache); return array($conn, $cache); } protected function setUp() { if (self::$skip) { $this->markTestSkipped(self::$reason); } list($this->_conn, $this->_cache) = self::getConnection(); self::$columnTest->conn = $this->_conn; self::$tableTest->conn = $this->_conn; // clear out detritus from any previous test runs. $this->_dropTestTables(); $table = $this->_conn->createTable('unit_tests'); $table->column('integer_value', 'integer', array('limit' => 11, 'default' => 0)); $table->column('string_value', 'string', array('limit' => 255, 'default' => '')); $table->column('text_value', 'text', array('null' => false, 'default' => '')); $table->column('float_value', 'float', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('decimal_value', 'decimal', array('precision' => 2, 'scale' => 1, 'default' => 0.0)); $table->column('datetime_value', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('date_value', 'date', array('default' => '0000-00-00')); $table->column('time_value', 'time', array('default' => '00:00:00')); $table->column('blob_value', 'binary', array('null' => false, 'default' => '')); $table->column('boolean_value', 'boolean', array('default' => false)); $table->column('email_value', 'string', array('limit' => 255, 'default' => '')); $table->end(); $this->_conn->addIndex('unit_tests', 'string_value', array('name' => 'string_value')); $this->_conn->addIndex('unit_tests', 'integer_value', array('name' => 'integer_value', 'unique' => true)); $this->_conn->addIndex('unit_tests', array('integer_value', 'string_value'), array('name' => 'integer_string')); // read sql file for statements $statements = array(); $current_stmt = ''; $fp = fopen(__DIR__ . '/../fixtures/unit_tests.sql', 'r'); while ($line = fgets($fp, 8192)) { $line = rtrim(preg_replace('/^(.*)--.*$/s', '\1', $line)); if (!$line) { continue; } $current_stmt .= $line; if (substr($line, -1) == ';') { // leave off the ending ; $statements[] = substr($current_stmt, 0, -1); $current_stmt = ''; } } // run statements foreach ($statements as $stmt) { $this->_conn->execute($stmt); } } protected function tearDown() { // clean up $this->_dropTestTables(); // close connection $this->_conn->disconnect(); } /*########################################################################## # Connection ##########################################################################*/ public function testConnect() { $this->assertTrue($this->_conn->isActive()); } public function testDisconnect() { $this->_conn->disconnect(); $this->assertFalse($this->_conn->isActive()); $this->_conn->connect(); $this->assertTrue($this->_conn->isActive()); } public function testReconnect() { $this->_conn->reconnect(); $this->assertTrue($this->_conn->isActive()); } /*########################################################################## # Accessor ##########################################################################*/ public function testAdapterName() { $this->assertEquals('MySQL', $this->_conn->adapterName()); } public function testSupportsMigrations() { $this->assertTrue($this->_conn->supportsMigrations()); } public function testSupportsCountDistinct() { $this->assertTrue($this->_conn->supportsCountDistinct()); } public function testGetCharset() { $this->assertEquals('utf8', strtolower($this->_conn->getCharset())); } /*########################################################################## # Database Statements ##########################################################################*/ public function testExecute() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->execute($sql); $row = mysql_fetch_assoc($result); $this->assertEquals(1, $row['id']); } public function testSelect() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->select($sql); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParameters() { $sql = "SELECT * FROM unit_tests WHERE id=?"; $result = $this->_conn->select($sql, array(1)); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectWithBoundParametersQuotesString() { $sql = "SELECT * FROM unit_tests WHERE string_value=?"; $result = $this->_conn->select($sql, array('name a')); $this->assertInstanceOf('Traversable', $result); $this->assertGreaterThan(0, count($result)); foreach ($result as $row) break; $this->assertInternalType('array', $row); $this->assertEquals(1, $row['id']); } public function testSelectAll() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectAll($sql); $this->assertInternalType('array', $result); $this->assertGreaterThan(0, count($result)); $this->assertEquals(1, $result[0]['id']); } public function testSelectOne() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectOne($sql); $this->assertEquals(1, $result['id']); } public function testSelectValue() { $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectValue($sql); $this->assertEquals(1, $result); } public function testSelectValues() { $sql = "SELECT * FROM unit_tests"; $result = $this->_conn->selectValues($sql); $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result); } public function testInsert() { $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $result = $this->_conn->insert($sql); $this->assertEquals(7, $result); } public function testUpdate() { $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)"; $result = $this->_conn->update($sql); $this->assertEquals(1, $result); } public function testDelete() { $sql = "DELETE FROM unit_tests WHERE id IN (1,2)"; $result = $this->_conn->delete($sql); $this->assertEquals(2, $result); } public function testTransactionStarted() { $this->assertFalse($this->_conn->transactionStarted()); $this->_conn->beginDbTransaction(); $this->assertTrue($this->_conn->transactionStarted()); $this->_conn->commitDbTransaction(); $this->assertFalse($this->_conn->transactionStarted()); } public function testTransactionCommit() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql); $this->_conn->commitDbTransaction(); // make sure it inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals('999', $this->_conn->selectValue($sql)); } public function testTransactionRollback() { $this->_conn->beginDbTransaction(); $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)"; $this->_conn->insert($sql); $this->_conn->rollbackDbTransaction(); // make sure it inserted $sql = "SELECT integer_value FROM unit_tests WHERE id='7'"; $this->assertEquals(null, $this->_conn->selectValue($sql)); } /*########################################################################## # Quoting ##########################################################################*/ public function testQuoteNull() { $this->assertEquals('NULL', $this->_conn->quote(null)); } public function testQuoteTrue() { $this->assertEquals('1', $this->_conn->quote(true)); } public function testQuoteFalse() { $this->assertEquals('0', $this->_conn->quote(false)); } public function testQuoteInteger() { $this->assertEquals('42', $this->_conn->quote(42)); } public function testQuoteFloat() { $this->assertEquals('42.2', $this->_conn->quote(42.2)); setlocale(LC_NUMERIC, 'de_DE.UTF-8'); $this->assertEquals('42.2', $this->_conn->quote(42.2)); } public function testQuoteString() { $this->assertEquals("'my string'", $this->_conn->quote('my string')); } public function testQuoteDirtyString() { $this->assertEquals("'derek\'s string'", $this->_conn->quote('derek\'s string')); } public function testQuoteColumnName() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)'); $this->assertEquals('1', $this->_conn->quote(true, $col)); } public function testQuoteBinary() { // Test string is foo\0ba'r - should be 8 bytes long $original = base64_decode('Zm9vAGJhJ3I='); $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); $this->_conn->insert('INSERT INTO binary_testings (data) VALUES (?)', array(new Horde_Db_Value_Binary($original))); $retrieved = $this->_conn->selectValue('SELECT data FROM binary_testings'); $columns = $this->_conn->columns('binary_testings'); $retrieved = $columns['data']->binaryToString($retrieved); $this->assertEquals($original, $retrieved); } /*########################################################################## # Schema Statements ##########################################################################*/ /** * We specifically do a manual INSERT here, and then test only the SELECT * functionality. This allows us to more easily catch INSERT being broken, * but SELECT actually working fine. */ public function testNativeDecimalInsertManualVsAutomatic() { $this->_createTestUsersTable(); $correctValue = 12345678901234567890.0123456789; $this->_conn->addColumn("users", "wealth", 'decimal', array('precision' => 30, 'scale' => 10)); // do a manual insertion $this->_conn->execute("INSERT INTO users (wealth) VALUES ('12345678901234567890.0123456789')"); // SELECT @todo - type cast attribute values $user = (object)$this->_conn->selectOne('SELECT * FROM users'); // assert_kind_of BigDecimal, row.wealth // If this assert fails, that means the SELECT is broken! $this->assertEquals($correctValue, $user->wealth); // Reset to old state $this->_conn->delete('DELETE FROM users'); // Now use the Adapter insertion $this->_conn->insert('INSERT INTO users (wealth) VALUES (12345678901234567890.0123456789)'); // SELECT @todo - type cast attribute values $user = (object)$this->_conn->selectOne('SELECT * FROM users'); // assert_kind_of BigDecimal, row.wealth // If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken! $this->assertEquals($correctValue, $user->wealth); } public function testNativeTypes() { $this->_createTestUsersTable(); $this->_conn->addColumn("users", "last_name", 'string'); $this->_conn->addColumn("users", "bio", 'text'); $this->_conn->addColumn("users", "age", 'integer'); $this->_conn->addColumn("users", "height", 'float'); $this->_conn->addColumn("users", "wealth", 'decimal', array('precision' => '30', 'scale' => '10')); $this->_conn->addColumn("users", "birthday", 'datetime'); $this->_conn->addColumn("users", "favorite_day", 'date'); $this->_conn->addColumn("users", "moment_of_truth", 'datetime'); $this->_conn->addColumn("users", "male", 'boolean'); $this->_conn->insert('INSERT INTO users (first_name, last_name, bio, age, height, wealth, birthday, favorite_day, moment_of_truth, male, company_id) ' . "VALUES ('bob', 'bobsen', 'I was born ....', 18, 1.78, 12345678901234567890.0123456789, '2005-01-01 12:23:40', '1980-03-05', '1582-10-10 21:40:18', 1, 1)"); $bob = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('bob', $bob->first_name); $this->assertEquals('bobsen', $bob->last_name); $this->assertEquals('I was born ....', $bob->bio); $this->assertEquals(18, $bob->age); // Test for 30 significent digits (beyond the 16 of float), 10 of them // after the decimal place. $this->assertEquals('12345678901234567890.0123456789', $bob->wealth); $this->assertEquals(1, $bob->male); // @todo - type casting } public function testNativeDatabaseTypes() { $types = $this->_conn->nativeDatabaseTypes(); $this->assertEquals(array('name' => 'int', 'limit' => 11), $types['integer']); } public function testUnabstractedDatabaseDependentTypes() { $this->_createTestUsersTable(); $this->_conn->delete('DELETE FROM users'); $this->_conn->addColumn('users', 'intelligence_quotient', 'tinyint'); $this->_conn->insert('INSERT INTO users (intelligence_quotient) VALUES (300)'); $jonnyg = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('127', $jonnyg->intelligence_quotient); } public function testTableAliasLength() { $len = $this->_conn->tableAliasLength(); $this->assertEquals(255, $len); } public function testTableAliasFor() { $alias = $this->_conn->tableAliasFor('my_table_name'); $this->assertEquals('my_table_name', $alias); } public function testTables() { $tables = $this->_conn->tables(); $this->assertTrue(count($tables) > 0); $this->assertContains('unit_tests', $tables); } public function testPrimaryKey() { $pk = $this->_conn->primaryKey('unit_tests'); $this->assertEquals('id', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->assertEquals('id', $pk->columns[0]); $table = $this->_conn->createTable('pk_tests', array('autoincrementKey' => false)); $table->column('foo', 'string'); $table->column('bar', 'string'); $table->end(); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', 'foo'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo', (string)$pk); $this->assertEquals(1, count($pk->columns)); $this->_conn->removePrimaryKey('pk_tests'); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEmpty((string)$pk); $this->assertEquals(0, count($pk->columns)); $this->_conn->addPrimaryKey('pk_tests', array('foo', 'bar')); $pk = $this->_conn->primaryKey('pk_tests'); $this->assertEquals('foo,bar', (string)$pk); } public function testIndexes() { $indexes = $this->_conn->indexes('unit_tests'); $this->assertEquals(3, count($indexes)); // unique index $col = array('integer_value'); $this->assertEquals('unit_tests', $indexes[0]->table); $this->assertEquals('integer_value', $indexes[0]->name); $this->assertEquals(true, $indexes[0]->unique); $this->assertEquals($col, $indexes[0]->columns); // normal index $col = array('string_value'); $this->assertEquals('unit_tests', $indexes[1]->table); $this->assertEquals('string_value', $indexes[1]->name); $this->assertEquals(false, $indexes[1]->unique); $this->assertEquals($col, $indexes[1]->columns); // multi-column index $col = array('integer_value', 'string_value'); $this->assertEquals('unit_tests', $indexes[2]->table); $this->assertEquals('integer_string', $indexes[2]->name); $this->assertEquals(false, $indexes[2]->unique); $this->assertEquals($col, $indexes[2]->columns); } public function testColumns() { $columns = $this->_conn->columns('unit_tests'); $this->assertEquals(12, count($columns)); $col = $columns['id']; $this->assertEquals('id', $col->getName()); $this->assertEquals('integer', $col->getType()); $this->assertEquals(false, $col->isNull()); $this->assertEquals(10, $col->getLimit()); $this->assertEquals(true, $col->isUnsigned()); $this->assertEquals('', $col->getDefault()); $this->assertEquals('int(10) unsigned', $col->getSqlType()); $this->assertEquals(false, $col->isText()); $this->assertEquals(true, $col->isNumber()); } public function testCreateTable() { $this->_createTestTable('sports'); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableNoPk() { $this->_createTestTable('sports', array('autoincrementKey' => false)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertNull($this->_conn->selectValue($sql)); } catch (Exception $e) { return; } $this->fail("Expected exception for no pk"); } public function testCreateTableWithNamedPk() { $this->_createTestTable('sports', array('autoincrementKey' => 'sports_id')); $sql = "SELECT sports_id FROM sports WHERE sports_id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertNull($this->_conn->selectValue($sql)); } catch (Exception $e) { return; } $this->fail("Expected exception for wrong pk name"); } public function testCreateTableWithSeparatePk() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'autoincrementKey'); $pkColumn = $table['foo']; $this->assertEquals('`foo` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', $pkColumn->toSql()); } public function testCreateTableCompositePk() { $table = $this->_conn->createTable('testings', array('autoincrementKey' => array('a_id', 'b_id'))); $table->column('a_id', 'integer'); $table->column('b_id', 'integer'); $table->end(); $pk = $this->_conn->primaryKey('testings'); $this->assertEquals(array('a_id', 'b_id'), $pk->columns); } public function testCreateTableForce() { $this->_createTestTable('sports'); $this->_createTestTable('sports', array('force' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableTemporary() { $this->_createTestTable('sports', array('temporary' => true)); $sql = "SELECT id FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testCreateTableAddsId() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[] = $col->getName(); } sort($columns); $this->assertEquals(array('foo', 'id'), $columns); } public function testCreateTableWithNotNullColumn() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('null' => false)); $table->end(); try { $this->_conn->execute("INSERT INTO testings (foo) VALUES (NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testCreateTableWithDefaults() { $table = $this->_conn->createTable('testings'); $table->column('one', 'string', array('default' => 'hello')); $table->column('two', 'boolean', array('default' => true)); $table->column('three', 'boolean', array('default' => false)); $table->column('four', 'integer', array('default' => 1)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals('hello', $columns['one']->getDefault()); $this->assertTrue($columns['two']->getDefault()); $this->assertFalse($columns['three']->getDefault()); $this->assertEquals(1, $columns['four']->getDefault()); } public function testCreateTableWithLimits() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string', array('limit' => 80)); $table->end(); $columns = array(); foreach ($this->_conn->columns('testings') as $col) { $columns[$col->getName()] = $col; } $this->assertEquals(80, $columns['foo']->getLimit()); } public function testCreateTableWithBinaryColumn() { try { $table = $this->_conn->createTable('binary_testings'); $table->column('data', 'binary', array('null' => false)); $table->end(); } catch (Exception $e) { $this->fail('Unexepected exception raised'); } $columns = $this->_conn->columns('binary_testings'); foreach ($columns as $c) { if ($c->getName() == 'data') { $dataColumn = $c; } } $this->assertEquals('', $dataColumn->getDefault()); } public function testRenameTable() { // Simple rename then select test $this->_createTestTable('sports'); $this->_conn->renameTable('sports', 'my_sports'); $sql = "SELECT id FROM my_sports WHERE id = 1"; $this->assertEquals("1", $this->_conn->selectValue($sql)); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); // Rename then insert test $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->execute($sql); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); // Make sure the old table name isn't still there try { $sql = "SELECT id FROM octopuses WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testRenameTableWithAnIndex() { $table = $this->_conn->createTable('octopuses'); $table->column('url', 'string'); $table->end(); $this->_conn->addIndex('octopuses', 'url'); $this->_conn->renameTable('octopuses', 'octopi'); $sql = "INSERT INTO octopi (id, url) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"; $this->_conn->execute($sql); $this->assertEquals('http://www.foreverflying.com/octopus-black7.jpg', $this->_conn->selectValue("SELECT url FROM octopi WHERE id=1")); $indexes = $this->_conn->indexes('octopi'); $this->assertEquals('url', $indexes[0]->columns[0]); } public function testDropTable() { $this->_createTestTable('sports'); $this->_conn->dropTable('sports'); try { $sql = "SELECT id FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Table exists where it shouldn't have"); } public function testAddColumn() { $this->_createTestTable('sports'); $this->_conn->addColumn('sports', 'modified_at', 'date'); $this->_conn->update("UPDATE sports SET modified_at = '2007-01-01'"); $sql = "SELECT modified_at FROM sports WHERE id = 1"; $this->assertEquals("2007-01-01", $this->_conn->selectValue($sql)); $this->_conn->addColumn('sports', 'with_default', 'integer', array('default' => 1)); $sql = "SELECT with_default FROM sports WHERE id = 1"; $this->assertEquals(1, $this->_conn->selectValue($sql)); } public function testRemoveColumn() { $this->_createTestTable('sports'); $sql = "SELECT name FROM sports WHERE id = 1"; $this->assertEquals("mlb", $this->_conn->selectValue($sql)); $this->_conn->removeColumn('sports', 'name'); try { $sql = "SELECT name FROM sports WHERE id = 1"; $this->_conn->execute($sql); } catch (Exception $e) { return; } $this->fail("Column exists where it shouldn't have"); } public function testChangeColumn() { $this->_createTestUsersTable(); $this->_conn->addColumn('users', 'age', 'integer'); $oldColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertTrue($found); $this->_conn->changeColumn('users', 'age', 'string'); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'integer') { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'age' && $c->getType() == 'string') { $found = true; } } $this->assertTrue($found); $found = false; foreach ($oldColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => false)); $newColumns = $this->_conn->columns('users', "User Columns"); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == true) { $found = true; } } $this->assertFalse($found); $found = false; foreach ($newColumns as $c) { if ($c->getName() == 'approved' && $c->getType() == 'boolean' && $c->getDefault() == false) { $found = true; } } $this->assertTrue($found); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'approved', 'boolean', array('default' => true)); // Test converting to autoincrementKey $this->_conn->changeColumn('users', 'id', 'integer'); $this->_conn->changeColumn('users', 'id', 'autoincrementKey'); // Test with non-existant primary key $this->_conn->changeColumn('users', 'id', 'integer'); $this->_conn->execute('ALTER TABLE users DROP PRIMARY KEY'); $this->_conn->changeColumn('users', 'id', 'autoincrementKey'); } public function testChangeColumnDefault() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'name'); $this->assertEquals('', $beforeChange->getDefault()); $this->_conn->changeColumnDefault('sports', 'name', 'test'); $afterChange = $this->_getColumn('sports', 'name'); $this->assertEquals('test', $afterChange->getDefault()); } public function testChangeColumnType() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string'); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar(255)', $afterChange->getSqlType()); $table = $this->_conn->createTable('text_to_binary'); $table->column('data', 'text'); $table->end(); $this->_conn->insert('INSERT INTO text_to_binary (data) VALUES (?)', array("foo\0bar")); $this->_conn->changeColumn('text_to_binary', 'data', 'binary'); $afterChange = $this->_getColumn('text_to_binary', 'data'); $this->assertEquals('longblob', $afterChange->getSqlType()); $this->assertEquals( "foo\0bar", $this->_conn->selectValue('SELECT data FROM text_to_binary')); } public function testChangeColumnLimit() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string', array('limit' => '40')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar(40)', $afterChange->getSqlType()); } public function testChangeColumnPrecisionScale() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'decimal', array('precision' => '5', 'scale' => '2')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('decimal(5,2)', $afterChange->getSqlType()); } public function testChangeColumnUnsigned() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'integer'); $table->end(); $beforeChange = $this->_getColumn('testings', 'foo'); $this->assertFalse($beforeChange->isUnsigned()); $this->_conn->execute("INSERT INTO testings (id, foo) VALUES (1, -1)"); $this->_conn->changeColumn('testings', 'foo', 'integer', array('unsigned' => true)); $afterChange = $this->_getColumn('testings', 'foo'); $this->assertTrue($afterChange->isUnsigned()); $row = (object)$this->_conn->selectOne('SELECT * FROM testings'); $this->assertEquals(0, $row->foo); } public function testRenameColumn() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'nick_name'); $this->assertTrue(in_array('nick_name', $this->_columnNames('users'))); $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('tinyint(1)', $beforeChange->getSqlType()); $this->_conn->renameColumn('sports', 'is_college', 'is_renamed'); $afterChange = $this->_getColumn('sports', 'is_renamed'); $this->assertEquals('tinyint(1)', $afterChange->getSqlType()); } public function testRenameColumnWithSqlReservedWord() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'group'); $this->assertTrue(in_array('group', $this->_columnNames('users'))); } public function testAddIndex() { $this->_createTestUsersTable(); // Limit size of last_name and key columns to support Firebird index limitations $this->_conn->addColumn('users', 'last_name', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'key', 'string', array('limit' => 100)); $this->_conn->addColumn('users', 'administrator', 'boolean'); $this->_conn->addIndex('users', 'last_name'); $this->_conn->removeIndex('users', 'last_name'); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('column' => array('last_name', 'first_name'))); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', array('name' => 'index_users_on_last_name_and_first_name')); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', 'last_name_and_first_name'); // quoting $this->_conn->addIndex('users', array('key'), array('name' => 'key_idx', 'unique' => true)); $this->_conn->removeIndex('users', array('name' => 'key_idx', 'unique' => true)); $this->_conn->addIndex('users', array('last_name', 'first_name', 'administrator'), array('name' => "named_admin")); $this->_conn->removeIndex('users', array('name' => 'named_admin')); } public function testAddIndexDefault() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); } public function testAddIndexMultiColumn() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); } public function testAddIndexUnique() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('unique' => true)); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertTrue($index->unique); } public function testAddIndexName() { $this->_createTestTable('sports'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); $this->_conn->addIndex('sports', 'is_college', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); $this->assertEquals('test', $index->name); } public function testRemoveIndexSingleColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college'); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => 'is_college')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testRemoveIndexMultiColumn() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', array('name', 'is_college')); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('column' => array('name', 'is_college'))); $index = $this->_getIndex('sports', array('name', 'is_college')); $this->assertNull($index); } public function testRemoveIndexByName() { $this->_createTestTable('sports'); // add the index $this->_conn->addIndex('sports', 'is_college', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNotNull($index); // remove it again $this->_conn->removeIndex('sports', array('name' => 'test')); $index = $this->_getIndex('sports', 'is_college'); $this->assertNull($index); } public function testIndexNameInvalid() { try { $name = $this->_conn->indexName('sports'); } catch (Horde_Db_Exception $e) { return; } $this->fail("Adding an index with crappy options worked where it shouldn't have"); } public function testIndexNameBySingleColumn() { $name = $this->_conn->indexName('sports', array('column' => 'is_college')); $this->assertEquals('index_sports_on_is_college', $name); } public function testIndexNameByMultiColumn() { $name = $this->_conn->indexName('sports', array('column' => array('name', 'is_college'))); $this->assertEquals('index_sports_on_name_and_is_college', $name); } public function testIndexNameByName() { $name = $this->_conn->indexName('sports', array('name' => 'test')); $this->assertEquals('test', $name); } public function testTypeToSqlTypePrimaryKey() { $result = $this->_conn->typeToSql('autoincrementKey'); $this->assertEquals('int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', $result); } public function testTypeToSqlTypeString() { $result = $this->_conn->typeToSql('string'); $this->assertEquals('varchar(255)', $result); } public function testTypeToSqlTypeText() { $result = $this->_conn->typeToSql('text'); $this->assertEquals('text', $result); } public function testTypeToSqlTypeBinary() { $result = $this->_conn->typeToSql('binary'); $this->assertEquals('longblob', $result); } public function testTypeToSqlTypeFloat() { $result = $this->_conn->typeToSql('float'); $this->assertEquals('float', $result); } public function testTypeToSqlTypeDatetime() { $result = $this->_conn->typeToSql('datetime'); $this->assertEquals('datetime', $result); } public function testTypeToSqlTypeTimestamp() { $result = $this->_conn->typeToSql('timestamp'); $this->assertEquals('datetime', $result); } public function testTypeToSqlInt() { $result = $this->_conn->typeToSql('integer'); $this->assertEquals('int(11)', $result); } public function testTypeToSqlIntUnsigned() { $result = $this->_conn->typeToSql('integer', null, null, null, true); $this->assertEquals('int(10) UNSIGNED', $result); } public function testTypeToSqlIntLimit() { $result = $this->_conn->typeToSql('integer', '1'); $this->assertEquals('int(1)', $result); } public function testTypeToSqlDecimalPrecision() { $result = $this->_conn->typeToSql('decimal', null, '5'); $this->assertEquals('decimal(5)', $result); } public function testTypeToSqlDecimalScale() { $result = $this->_conn->typeToSql('decimal', null, '5', '2'); $this->assertEquals('decimal(5, 2)', $result); } public function testTypeToSqlBoolean() { $result = $this->_conn->typeToSql('boolean'); $this->assertEquals('tinyint(1)', $result); } public function testAddColumnOptions() { $result = $this->_conn->addColumnOptions("test", array()); $this->assertEquals("test", $result); } public function testAddColumnOptionsDefault() { $options = array('default' => '0'); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test DEFAULT '0'", $result); } public function testAddColumnOptionsNull() { $options = array('null' => true); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test", $result); } public function testAddColumnOptionsNotNull() { $options = array('null' => false); $result = $this->_conn->addColumnOptions("test", $options); $this->assertEquals("test NOT NULL", $result); } public function testAddColumnNotNullWithoutDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => '')); try { $this->_conn->execute("INSERT INTO testings (foo, bar) VALUES ('hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddColumnNotNullWithDefault() { $table = $this->_conn->createTable('testings'); $table->column('foo', 'string'); $table->end(); $this->_conn->execute("INSERT INTO testings (id, foo) VALUES ('1', 'hello')"); $this->_conn->addColumn('testings', 'bar', 'string', array('null' => false, 'default' => 'default')); try { $this->_conn->execute("INSERT INTO testings (id, foo, bar) VALUES (2, 'hello', NULL)"); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testAddRemoveSingleField() { $this->_createTestUsersTable(); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); $this->_conn->addColumn('users', 'last_name', 'string'); $this->assertTrue(in_array('last_name', $this->_columnNames('users'))); $this->_conn->removeColumn('users', 'last_name'); $this->assertFalse(in_array('last_name', $this->_columnNames('users'))); } public function testAddRename() { $this->_createTestUsersTable(); $this->_conn->delete('DELETE FROM users'); $this->_conn->addColumn('users', 'girlfriend', 'string'); $this->_conn->insert("INSERT INTO users (girlfriend) VALUES ('bobette')"); $this->_conn->renameColumn('users', 'girlfriend', 'exgirlfriend'); $bob = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('bobette', $bob->exgirlfriend); } public function testDistinct() { $result = $this->_conn->distinct("test"); $this->assertEquals("DISTINCT test", $result); } public function testAddOrderByForAssocLimiting() { $result = $this->_conn->addOrderByForAssocLimiting("SELECT * FROM documents ", array('order' => 'name DESC')); $this->assertEquals("SELECT * FROM documents ORDER BY name DESC", $result); } public function testInterval() { $this->assertEquals('INTERVAL 1 DAY', $this->_conn->interval('1 DAY', '')); } public function testModifyDate() { $modifiedDate = $this->_conn->modifyDate('start', '+', 1, 'DAY'); $this->assertEquals('start + INTERVAL \'1\' DAY', $modifiedDate); $t = $this->_conn->createTable('dates'); $t->column('start', 'datetime'); $t->column('end', 'datetime'); $t->end(); $this->_conn->insert( 'INSERT INTO dates (start, end) VALUES (?, ?)', array( '2011-12-10 00:00:00', '2011-12-11 00:00:00' ) ); $this->assertEquals( 1, $this->_conn->selectValue('SELECT COUNT(*) FROM dates WHERE ' . $modifiedDate . ' = end') ); } public function testBuildClause() { $this->assertEquals( 'bitmap & 2', $this->_conn->buildClause('bitmap', '&', 2)); $this->assertEquals( array('bitmap & ?', array(2)), $this->_conn->buildClause('bitmap', '&', 2, true)); $this->assertEquals( 'bitmap | 2', $this->_conn->buildClause('bitmap', '|', 2)); $this->assertEquals( array('bitmap | ?', array(2)), $this->_conn->buildClause('bitmap', '|', 2, true)); $this->assertEquals( "LOWER(name) LIKE LOWER('%search%')", $this->_conn->buildClause('name', 'LIKE', "search")); $this->assertEquals( array("LOWER(name) LIKE LOWER(?)", array('%search%')), $this->_conn->buildClause('name', 'LIKE', "search", true)); $this->assertEquals( "LOWER(name) LIKE LOWER('%search\&replace\?%')", $this->_conn->buildClause('name', 'LIKE', "search&replace?")); $this->assertEquals( array("LOWER(name) LIKE LOWER(?)", array('%search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true)); $this->assertEquals( "(LOWER(name) LIKE LOWER('search\&replace\?%') OR LOWER(name) LIKE LOWER('% search\&replace\?%'))", $this->_conn->buildClause('name', 'LIKE', "search&replace?", false, array('begin' => true))); $this->assertEquals( array("(LOWER(name) LIKE LOWER(?) OR LOWER(name) LIKE LOWER(?))", array('search&replace?%', '% search&replace?%')), $this->_conn->buildClause('name', 'LIKE', "search&replace?", true, array('begin' => true))); $this->assertEquals( 'value = 2', $this->_conn->buildClause('value', '=', 2)); $this->assertEquals( array('value = ?', array(2)), $this->_conn->buildClause('value', '=', 2, true)); $this->assertEquals( "value = 'foo'", $this->_conn->buildClause('value', '=', 'foo')); $this->assertEquals( array('value = ?', array('foo')), $this->_conn->buildClause('value', '=', 'foo', true)); $this->assertEquals( "value = 'foo\?bar'", $this->_conn->buildClause('value', '=', 'foo?bar')); $this->assertEquals( array('value = ?', array('foo?bar')), $this->_conn->buildClause('value', '=', 'foo?bar', true)); } public function testInsertAndReadInCp1257() { list($conn,) = self::getConnection(array('charset' => 'cp1257')); $table = $conn->createTable('charset_cp1257'); $table->column('text', 'string'); $table->end(); $input = file_get_contents(__DIR__ . '/../fixtures/charsets/cp1257.txt'); $conn->insert("INSERT INTO charset_cp1257 (text) VALUES (?)", array($input)); $output = $conn->selectValue('SELECT text FROM charset_cp1257'); $this->assertEquals($input, $output); } public function testInsertAndReadInUtf8() { list($conn,) = self::getConnection(array('charset' => 'utf8')); $table = $conn->createTable('charset_utf8'); $table->column('text', 'string'); $table->end(); $input = file_get_contents(__DIR__ . '/../fixtures/charsets/utf8.txt'); $conn->insert("INSERT INTO charset_utf8 (text) VALUES (?)", array($input)); $output = $conn->selectValue('SELECT text FROM charset_utf8'); $this->assertEquals($input, $output); } /*########################################################################## # Table cache ##########################################################################*/ public function testCachedTableIndexes() { // remove any current cache. $this->_cache->set('tables/indexes/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/indexes/cache_table', 0)); $this->_createTestTable('cache_table'); $idxs = $this->_conn->indexes('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/indexes/cache_table', 0)); } public function testCachedTableColumns() { // remove any current cache. $this->_cache->set('tables/columns/cache_table', ''); $this->assertEquals('', $this->_cache->get('tables/columns/cache_table', 0)); $this->_createTestTable('cache_table'); $cols = $this->_conn->columns('cache_table'); $this->assertNotEquals('', $this->_cache->get('tables/columns/cache_table', 0)); } /*########################################################################## # Protected ##########################################################################*/ /** * Create table to perform tests on */ protected function _createTestTable($name, $options=array()) { $table = $this->_conn->createTable($name, $options); $table->column('name', 'string'); $table->column('is_college', 'boolean'); $table->end(); try { // make sure table was created $sql = "INSERT INTO $name VALUES (1, 'mlb', 0)"; $this->_conn->insert($sql); } catch (Exception $e) {} } protected function _createTestUsersTable() { $table = $this->_conn->createTable('users'); $table->column('company_id', 'integer', array('limit' => 11)); $table->column('name', 'string', array('limit' => 255, 'default' => '')); $table->column('first_name', 'string', array('limit' => 40, 'default' => '')); $table->column('approved', 'boolean', array('default' => true)); $table->column('type', 'string', array('limit' => 255, 'default' => '')); $table->column('created_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('created_on', 'date', array('default' => '0000-00-00')); $table->column('updated_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('updated_on', 'date', array('default' => '0000-00-00')); $table->end(); } /** * drop test tables */ protected function _dropTestTables() { $tables = array( 'binary_testings', 'cache_table', 'charset_cp1257', 'charset_utf8', 'dates', 'my_sports', 'octopi', 'pk_tests', 'schema_info', 'sports', 'testings', 'text_to_binary', 'unit_tests', 'users', ); foreach ($tables as $table) { try { $this->_conn->dropTable($table); } catch (Exception $e) {} } } protected function _columnNames($tableName) { $columns = array(); foreach ($this->_conn->columns($tableName) as $c) { $columns[] = $c->getName(); } return $columns; } /** * Get a column by name */ protected function _getColumn($table, $column) { foreach ($this->_conn->columns($table) as $col) { if ($col->getName() == $column) return $col; } } /** * Get an index by columns */ protected function _getIndex($table, $indexes) { $indexes = (array) $indexes; sort($indexes); foreach ($this->_conn->indexes($table) as $index) { $columns = $index->columns; sort($columns); if ($columns == $indexes) return $index; } } public function testColumnConstruct() { self::$columnTest->testConstruct(); } public function testColumnToSql() { self::$columnTest->testToSql(); } public function testColumnToSqlLimit() { self::$columnTest->testToSqlLimit(); } public function testColumnToSqlPrecisionScale() { self::$columnTest->testToSqlPrecisionScale(); } public function testColumnToSqlUnsigned() { self::$columnTest->testToSqlUnsigned(); } public function testColumnToSqlNotNull() { self::$columnTest->testToSqlNotNull(); } public function testColumnToSqlDefault() { self::$columnTest->testToSqlDefault(); } public function testTableConstruct() { self::$tableTest->testConstruct(); } public function testTableName() { self::$tableTest->testName(); } public function testTableGetOptions() { self::$tableTest->testGetOptions(); } public function testTablePrimaryKey() { self::$tableTest->testPrimaryKey(); } public function testTableColumn() { self::$tableTest->testColumn(); } public function testTableToSql() { self::$tableTest->testToSql(); } } Horde_Db-2.0.4/test/Horde/Db/fixtures/charsets/cp1257.txt0000644000175000017500000000001112205351473020773 0ustar janjanAð þinau Horde_Db-2.0.4/test/Horde/Db/fixtures/charsets/utf8.txt0000644000175000017500000000000412205351473020742 0ustar janjan☃ Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations/1_users_have_last_names1.php0000644000175000017500000000037612205351473025253 0ustar janjanaddColumn('users', 'last_name', 'string'); } public function down() { $this->removeColumn('users', 'last_name'); } }Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations/2_we_need_reminders1.php0000644000175000017500000000052412205351473024353 0ustar janjancreateTable('reminders'); $t->column('content', 'text'); $t->column('remind_at', 'datetime'); $t->end(); } public function down() { $this->dropTable('reminders'); } }Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations/3_innocent_jointable1.php0000644000175000017500000000061412205351473024542 0ustar janjancreateTable('users_reminders', array('autoincrementKey' => false)); $t->column('reminder_id', 'integer'); $t->column('user_id', 'integer'); $t->end(); } public function down() { $this->dropTable('users_reminders'); } }Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations_with_decimal/1_give_me_big_numbers.php0000644000175000017500000000127312205351473027315 0ustar janjancreateTable('big_numbers'); $table->column('bank_balance', 'decimal', array('precision' => 10, 'scale' => 2)); $table->column('big_bank_balance', 'decimal', array('precision' => 15, 'scale' => 2)); $table->column('world_population', 'decimal', array('precision' => 10)); $table->column('my_house_population', 'decimal', array('precision' => 2)); $table->column('value_of_e', 'decimal'); $table->end(); } public function down() { $this->dropTable('big_numbers'); } }Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations_with_duplicate/1_users_have_last_names2.php0000644000175000017500000000037612205351473030341 0ustar janjanaddColumn('users', 'last_name', 'string'); } public function down() { $this->removeColumn('users', 'last_name'); } }Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations_with_duplicate/2_we_need_reminders2.php0000644000175000017500000000052412205351473027441 0ustar janjancreateTable('reminders'); $t->column('content', 'text'); $t->column('remind_at', 'datetime'); $t->end(); } public function down() { $this->dropTable('reminders'); } }Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations_with_duplicate/3_foo.php0000644000175000017500000000020112205351473024455 0ustar janjancreateTable('users_reminders', array('autoincrementKey' => false)); $t->column('reminder_id', 'integer'); $t->column('user_id', 'integer'); $t->end(); } public function down() { $this->dropTable('users_reminders'); } }Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations_with_missing_versions/1_users_have_last_names3.php0000644000175000017500000000037612205351473031771 0ustar janjanaddColumn('users', 'last_name', 'string'); } public function down() { $this->removeColumn('users', 'last_name'); } }Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations_with_missing_versions/3_we_need_reminders.php0000644000175000017500000000052312205351473031006 0ustar janjancreateTable('reminders'); $t->column('content', 'text'); $t->column('remind_at', 'datetime'); $t->end(); } public function down() { $this->dropTable('reminders'); } } Horde_Db-2.0.4/test/Horde/Db/fixtures/migrations_with_missing_versions/4_innocent_jointable3.php0000644000175000017500000000061412205351473031261 0ustar janjancreateTable('users_reminders', array('autoincrementKey' => false)); $t->column('reminder_id', 'integer'); $t->column('user_id', 'integer'); $t->end(); } public function down() { $this->dropTable('users_reminders'); } } ././@LongLink000 147 0003740 LHorde_Db-2.0.4/test/Horde/Db/fixtures/migrations_with_missing_versions/1000_users_have_middle_names.phpHorde_Db-2.0.4/test/Horde/Db/fixtures/migrations_with_missing_versions/1000_users_have_middle_names.0000644000175000017500000000040312205351473031700 0ustar janjanaddColumn('users', 'middle_name', 'string'); } public function down() { $this->removeColumn('users', 'middle_name'); } }Horde_Db-2.0.4/test/Horde/Db/fixtures/drop_create_table.sql0000644000175000017500000000114012205351473021660 0ustar janjan-- MySQL dump 10.11 -- -- Host: localhost Database: eemaster -- ------------------------------------------------------ -- Server version 5.0.27-standard -- -- Table structure for table `exp_actions` -- DROP TABLE IF EXISTS `exp_actions`; SET @saved_cs_client = @@character_set_client; SET character_set_client = utf8; CREATE TABLE `exp_actions` ( `action_id` int(4) unsigned NOT NULL auto_increment, `class` varchar(50) NOT NULL, `method` varchar(50) NOT NULL, PRIMARY KEY (`action_id`) ) ENGINE=MyISAM AUTO_INCREMENT=20 DEFAULT CHARSET=latin1; SET character_set_client = @saved_cs_client; Horde_Db-2.0.4/test/Horde/Db/fixtures/unit_tests.sql0000664000175000017500000002355112205351473020437 0ustar janjan-- -- Copyright 2007 Maintainable Software, LLC -- Copyright 2008-2013 Horde LLC (http://www.horde.org/) -- -- @author Mike Naberezny -- @author Derek DeVries -- @author Chuck Hagenbuch -- @license http://www.horde.org/licenses/bsd -- @category Horde -- @package Db -- @subpackage UnitTests -- INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('1', '1', 'name a', 'string a', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '1', 'foo@example.com'); INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('2', '2', 'name b', 'string b', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '0', 'foo@example.com'); INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('3', '3', 'name c', 'string a', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '1', 'foo@example.com'); INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('4', '4', 'name d', 'string b', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '0', 'foo@example.com'); INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('5', '5', 'name e', 'string b', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '1', 'foo@example.com'); INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('6', '6', 'name f', 'string b', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data', '0', 'foo@example.com'); Horde_Db-2.0.4/test/Horde/Db/Migration/BaseTest.php0000664000175000017500000001454212205351473020020 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ require_once dirname(__DIR__) . '/fixtures/migrations/1_users_have_last_names1.php'; require_once dirname(__DIR__) . '/fixtures/migrations/2_we_need_reminders1.php'; require_once dirname(__DIR__) . '/fixtures/migrations_with_decimal/1_give_me_big_numbers.php'; /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Migration_BaseTest extends PHPUnit_Framework_TestCase { public function setUp() { try { $this->_conn = new Horde_Db_Adapter_Pdo_Sqlite(array( 'dbname' => ':memory:', )); } catch (Horde_Db_Exception $e) { $this->markTestSkipped('The sqlite adapter is not available'); } $table = $this->_conn->createTable('users'); $table->column('company_id', 'integer', array('limit' => 11)); $table->column('name', 'string', array('limit' => 255, 'default' => '')); $table->column('first_name', 'string', array('limit' => 40, 'default' => '')); $table->column('approved', 'boolean', array('default' => true)); $table->column('type', 'string', array('limit' => 255, 'default' => '')); $table->column('created_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('created_on', 'date', array('default' => '0000-00-00')); $table->column('updated_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('updated_on', 'date', array('default' => '0000-00-00')); $table->end(); } public function testChangeColumnWithNilDefault() { $this->_conn->addColumn('users', 'contributor', 'boolean', array('default' => true)); $users = $this->_conn->table('users'); $this->assertTrue($users->contributor->getDefault()); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'contributor', 'boolean', array('default' => null)); $users = $this->_conn->table('users'); $this->assertNull($users->contributor->getDefault()); } public function testChangeColumnWithNewDefault() { $this->_conn->addColumn('users', 'administrator', 'boolean', array('default' => true)); $users = $this->_conn->table('users'); $this->assertTrue($users->administrator->getDefault()); // changeColumn() throws exception on error $this->_conn->changeColumn('users', 'administrator', 'boolean', array('default' => false)); $users = $this->_conn->table('users'); $this->assertFalse($users->administrator->getDefault()); } public function testChangeColumnDefault() { $this->_conn->changeColumnDefault('users', 'first_name', 'Tester'); $users = $this->_conn->table('users'); $this->assertEquals('Tester', $users->first_name->getDefault()); } public function testChangeColumnDefaultToNull() { $this->_conn->changeColumnDefault('users', 'first_name', null); $users = $this->_conn->table('users'); $this->assertNull($users->first_name->getDefault()); } public function testAddTable() { $e = null; try { $this->_conn->selectValues("SELECT * FROM reminders"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); $m = new WeNeedReminders1($this->_conn); $m->up(); $this->_conn->insert("INSERT INTO reminders (content, remind_at) VALUES ('hello world', '2005-01-01 11:10:01')"); $reminder = (object)$this->_conn->selectOne('SELECT * FROM reminders'); $this->assertEquals('hello world', $reminder->content); $m->down(); $e = null; try { $this->_conn->selectValues("SELECT * FROM reminders"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); } public function testAddTableWithDecimals() { $e = null; try { $this->_conn->selectValues("SELECT * FROM big_numbers"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); $m = new GiveMeBigNumbers($this->_conn); $m->up(); $this->_conn->insert('INSERT INTO big_numbers (bank_balance, big_bank_balance, world_population, my_house_population, value_of_e) VALUES (1586.43, 1000234000567.95, 6000000000, 3, 2.7182818284590452353602875)'); $b = (object)$this->_conn->selectOne('SELECT * FROM big_numbers'); $this->assertNotNull($b->bank_balance); $this->assertNotNull($b->big_bank_balance); $this->assertNotNull($b->world_population); $this->assertNotNull($b->my_house_population); $this->assertNotNull($b->value_of_e); $m->down(); $e = null; try { $this->_conn->selectValues("SELECT * FROM big_numbers"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); } public function testAutoincrement() { $t = $this->_conn->createTable('imp_sentmail', array('autoincrementKey' => array('sentmail_id'))); $t->column('sentmail_id', 'bigint', array('null' => false)); $t->column('sentmail_foo', 'string'); $t->end(); $migration = new Horde_Db_Migration_Base($this->_conn, null); $migration->changeColumn('imp_sentmail', 'sentmail_id', 'autoincrementKey'); $columns = $this->_conn->columns('imp_sentmail'); $this->assertEquals(2, count($columns)); $this->assertTrue(isset($columns['sentmail_id'])); $this->assertEquals(array('sentmail_id'), $this->_conn->primaryKey('imp_sentmail')->columns); $this->_conn->insert('INSERT INTO imp_sentmail (sentmail_foo) VALUES (?)', array('bar')); } } Horde_Db-2.0.4/test/Horde/Db/Migration/MigratorTest.php0000664000175000017500000002000312205351473020717 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Mike Naberezny * @author Derek DeVries * @author Chuck Hagenbuch * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Migration_MigratorTest extends Horde_Test_Case { public function setUp() { try { $this->_conn = new Horde_Db_Adapter_Pdo_Sqlite(array( 'dbname' => ':memory:', )); } catch (Horde_Db_Exception $e) { $this->markTestSkipped('The sqlite adapter is not available'); } $table = $this->_conn->createTable('users'); $table->column('company_id', 'integer', array('limit' => 11)); $table->column('name', 'string', array('limit' => 255, 'default' => '')); $table->column('first_name', 'string', array('limit' => 40, 'default' => '')); $table->column('approved', 'boolean', array('default' => true)); $table->column('type', 'string', array('limit' => 255, 'default' => '')); $table->column('created_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('created_on', 'date', array('default' => '0000-00-00')); $table->column('updated_at', 'datetime', array('default' => '0000-00-00 00:00:00')); $table->column('updated_on', 'date', array('default' => '0000-00-00')); $table->end(); } public function testInitializeSchemaInformation() { $dir = dirname(__DIR__).'/fixtures/migrations/'; $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $sql = "SELECT version FROM schema_info"; $this->assertEquals(0, $this->_conn->selectValue($sql)); } public function testMigrator() { $columns = $this->_columnNames('users'); $this->assertFalse(in_array('last_name', $columns)); $e = null; try { $this->_conn->selectValues("SELECT * FROM reminders"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); $dir = dirname(__DIR__).'/fixtures/migrations/'; $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $migrator->up(); $this->assertEquals(3, $migrator->getCurrentVersion()); $columns = $this->_columnNames('users'); $this->assertTrue(in_array('last_name', $columns)); $this->_conn->insert("INSERT INTO reminders (content, remind_at) VALUES ('hello world', '2005-01-01 02:22:23')"); $reminder = (object)$this->_conn->selectOne('SELECT * FROM reminders'); $this->assertEquals('hello world', $reminder->content); $dir = dirname(__DIR__).'/fixtures/migrations/'; $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $migrator->down(); $this->assertEquals(0, $migrator->getCurrentVersion()); $columns = $this->_columnNames('users'); $this->assertFalse(in_array('last_name', $columns)); $e = null; try { $this->_conn->selectValues("SELECT * FROM reminders"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); } public function testOneUp() { $e = null; try { $this->_conn->selectValues("SELECT * FROM reminders"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); $dir = dirname(__DIR__).'/fixtures/migrations/'; $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $migrator->up(1); $this->assertEquals(1, $migrator->getCurrentVersion()); $columns = $this->_columnNames('users'); $this->assertTrue(in_array('last_name', $columns)); $e = null; try { $this->_conn->selectValues("SELECT * FROM reminders"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); $migrator->up(2); $this->assertEquals(2, $migrator->getCurrentVersion()); $this->_conn->insert("INSERT INTO reminders (content, remind_at) VALUES ('hello world', '2005-01-01 02:22:23')"); $reminder = (object)$this->_conn->selectOne('SELECT * FROM reminders'); $this->assertEquals('hello world', $reminder->content); } public function testOneDown() { $dir = dirname(__DIR__).'/fixtures/migrations/'; $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $migrator->up(); $migrator->down(1); $columns = $this->_columnNames('users'); $this->assertTrue(in_array('last_name', $columns)); } public function testOneUpOneDown() { $dir = dirname(__DIR__).'/fixtures/migrations/'; $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $migrator->up(1); $migrator->down(0); $columns = $this->_columnNames('users'); $this->assertFalse(in_array('last_name', $columns)); } public function testMigratorGoingDownDueToVersionTarget() { $dir = dirname(__DIR__).'/fixtures/migrations/'; $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $migrator->up(1); $migrator->down(0); $columns = $this->_columnNames('users'); $this->assertFalse(in_array('last_name', $columns)); $e = null; try { $this->_conn->selectValues("SELECT * FROM reminders"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $migrator->up(); $columns = $this->_columnNames('users'); $this->assertTrue(in_array('last_name', $columns)); $this->_conn->insert("INSERT INTO reminders (content, remind_at) VALUES ('hello world', '2005-01-01 02:22:23')"); $reminder = (object)$this->_conn->selectOne('SELECT * FROM reminders'); $this->assertEquals('hello world', $reminder->content); } public function testWithDuplicates() { try { $dir = dirname(__DIR__).'/fixtures/migrations_with_duplicate/'; $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $migrator->up(); } catch (Exception $e) { return; } $this->fail('Expected exception wasn\'t raised'); } public function testWithMissingVersionNumbers() { $dir = dirname(__DIR__).'/fixtures/migrations_with_missing_versions/'; $migrator = new Horde_Db_Migration_Migrator($this->_conn, null, array('migrationsPath' => $dir)); $migrator->migrate(500); $this->assertEquals(4, $migrator->getCurrentVersion()); $migrator->migrate(2); $this->assertEquals(2, $migrator->getCurrentVersion()); $e = null; try { $this->_conn->selectValues("SELECT * FROM reminders"); } catch (Exception $e) {} $this->assertInstanceOf('Horde_Db_Exception', $e); $columns = $this->_columnNames('users'); $this->assertTrue(in_array('last_name', $columns)); } protected function _columnNames($tableName) { $columns = array(); foreach ($this->_conn->columns($tableName) as $c) { $columns[] = $c->getName(); } return $columns; } } Horde_Db-2.0.4/test/Horde/Db/AllTests.php0000664000175000017500000000013212205351473016076 0ustar janjanrun(); Horde_Db-2.0.4/test/Horde/Db/bootstrap.php0000664000175000017500000000014312205351473016362 0ustar janjan Horde_Db-2.0.4/test/Horde/Db/StatementParserTest.php0000664000175000017500000000247512205351473020340 0ustar janjanassertParser($expected, 'drop_create_table.sql'); } public function assertParser(array $expectedStatements, $filename) { $file = new SplFileObject(__DIR__ . '/fixtures/' . $filename, 'r'); $parser = new Horde_Db_StatementParser($file); foreach ($expectedStatements as $i => $expected) { // Strip any whitespace before comparing the strings. $this->assertEquals(preg_replace('/\s/', '', $expected), preg_replace('/\s/', '', $parser->next()), "Parser differs on statement #$i"); } } }