package.xml0000664000175000017500000012235212653711336011313 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 2016-02-01 2.3.1 2.3.0 stable stable BSD-2-Clause * [jan] Bump earliest supported PostgreSQL version to 8.3. * [jan] Improve getting tables and indexes from PostgreSQL servers (Ivan Sergio Borgonovo <mail@webthatworks.it>). * [jan] Mark PHP 7 as supported. 5.3.0 8.0.0alpha1 8.0.0alpha1 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_Util 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 oci8 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. 2.1.0 2.1.0 stable stable 2014-03-03 BSD-2-Clause * [jan] Return generated (or manipulated) index name from addIndex(). * [jan] Avoid cache collisions when changing adapters or configurations. * [jan] Add Horde_Db_Adapter_Base#writeCache() and readCache(). * [jan] Add Horde_Db_Adapter#insertBlob(). * [jan] Make value of Horde_Db_Value_Binary publically accessible. * [jan] Add Oracle driver (Request #11974). * [jan] Add Horde_Db_Adapter_Base_Schema#column() method. * [jan] Always re-throw PDOExceptions as Horde_Db_Exceptions. * [jan] Fix issues with active Turkish locale. * [jan] Always return a Horde_Db_Adapter_Base_Result sub-class from the select() method. * [jan] Don't ping server before each query in PDO drivers (Bug #12656). * [jan] Re-organize unit tests. * [jan] Make several methods abstract in Horde_Db_Adapter_Base. * [jan] Deprecate external usage of execute() method. 2.1.1 2.1.0 stable stable 2014-03-31 BSD-2-Clause * [mms] Fix regression preventing caching of any internal DB information. * [mms] SECURITY: Don't leak PDO DSN authentication/connection information in exception messages. * [jan] Fix columnCount() result of PDO result objects. 2.1.2 2.1.0 stable stable 2014-05-21 BSD-2-Clause * [jan] Fix migrating large values to BLOB on Oracle. * [jan] Fix certain migrations with Oracle. 2.1.3 2.1.0 stable stable 2014-06-17 BSD-2-Clause * [jan] Fix changing columns to NULL/NOT NULL on Oracle and SQLite. 2.1.4 2.1.0 stable stable 2014-10-02 BSD-2-Clause * [jan] Catch exceptions from PDO result set methods. 2.1.5 2.1.0 stable stable 2014-10-23 BSD-2-Clause * [jan] Fix migrating NULL values to BLOB on Oracle. 2.2.0 2.2.0 stable stable 2014-11-06 BSD-2-Clause * [jan] Fix returning primary key value from insertBlob(). * [jan] Add Horde_Db_Adapter::updateBlob(). * [jan] Add Horde_Db_Value_Text to encapsulate long text values. * [jan] Add missing insertBlob to adapter interface. 2.2.1 2.2.0 stable stable 2014-11-18 BSD-2-Clause * [jan] Fix using addColumn() with autoincrementKey types in SQLite. * [jan] Fix several issues with changing autoincrementKey columns in Oracle. * [mms] Fix SplitRead driver to work with the recent blob changes. 2.2.2 2.2.0 stable stable 2014-11-18 BSD-2-Clause * [jan] Make Oracle's updateBlob() compatible with PHP 5.3. 2.2.3 2.2.0 stable stable 2015-04-28 BSD-2-Clause * [jan] Fix certain migrations on Oracle. * [jan] Fix issues with certain locales like Turkish. 2.3.0 2.3.0 stable stable 2015-11-17 BSD-2-Clause * [mjr] Fix SQL error when setting blob fields to empty in oracle driver (Bug #14163). * [jan] Add 'status' argument to horde-db-migrate-component (Nicolas Rochelemagne <nicolas.rochelemagne@cpanel.net>). 2.3.1 2.3.0 stable stable 2016-02-01 BSD-2-Clause * [jan] Bump earliest supported PostgreSQL version to 8.3. * [jan] Improve getting tables and indexes from PostgreSQL servers (Ivan Sergio Borgonovo <mail@webthatworks.it>). * [jan] Mark PHP 7 as supported. Horde_Db-2.3.1/bin/horde-db-migrate-component0000775000175000017500000000663712653711335017121 0ustar janjan#!/usr/bin/env php ) [debug]] * * Copyright 2010-2016 Horde LLC (http://www.horde.org/) * * See the enclosed file COPYING for license information (BSD). If you did * not receive this file, see http://www.horde.org/licenses/bsd * * @author Chuck Hagenbuch * @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|status|) [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 = Horde_String::lower($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; case 'status': $action = $args[2]; break; default: $action = 'migrate'; $targetVersion = $args[2]; break; } } // Build Horde_Db adapter. $class = 'Horde_Db_Adapter_' . str_replace(' ', '_', Horde_String::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 'status': echo 'Target schema version: ' . $migrator->getTargetVersion() . "\n"; exit( $migrator->getCurrentVersion() == $migrator->getTargetVersion() ? 0 : 1 ); 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.3.1/doc/Horde/Db/COPYING0000664000175000017500000000243012653711335014462 0ustar janjan Copyright 1999-2016 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.3.1/doc/Horde/Db/README_TESTING0000664000175000017500000000240612653711335015507 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.3.1/doc/Horde/Db/TODO0000664000175000017500000000367212653711335014130 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.3.1/doc/Horde/Db/UPGRADING0000664000175000017500000000145712653711335014702 0ustar janjan==================== Upgrading Horde_Db ==================== :Contact: dev@lists.horde.org .. contents:: Contents .. section-numbering:: This lists the API changes between releases of the package. Upgrading to 2.1.0 ================== - Horde_Db_Adapter_Base - The execute(), select(), insert(), beginDbTransaction(), commitDbTransaction(), and rollbackDbTransaction() methods are abstract now. - The execute() method has been deprecated for external usage. Use the select() method instead or one of the other query methods. - The select() method always returns a Horde_Db_Adapter_Base_Result sub-class now. - The addIndex() method returns the used index name now. - The writeCache(), readCache(), insertBlob(), and column() methods have been added. Horde_Db-2.3.1/lib/Horde/Db/Adapter/Base/Column.php0000664000175000017500000002304012653711335017630 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->_setSimplifiedType(); $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); } /** * @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 = Horde_String::lower($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); } /** */ protected function _setSimplifiedType() { switch (true) { case preg_match('/int/i', $this->_sqlType): $this->_type = 'integer'; return; case preg_match('/float|double/i', $this->_sqlType): $this->_type = 'float'; return; case preg_match('/decimal|numeric|number/i', $this->_sqlType): $this->_type = $this->_scale == 0 ? 'integer' : 'decimal'; return; case preg_match('/datetime/i', $this->_sqlType): $this->_type = 'datetime'; return; case preg_match('/timestamp/i', $this->_sqlType): $this->_type = 'timestamp'; return; case preg_match('/time/i', $this->_sqlType): $this->_type = 'time'; return; case preg_match('/date/i', $this->_sqlType): $this->_type = 'date'; return; case preg_match('/clob|text/i', $this->_sqlType): $this->_type = 'text'; return; case preg_match('/blob|binary/i', $this->_sqlType): $this->_type = 'binary'; return; case preg_match('/char|string/i', $this->_sqlType): $this->_type = 'string'; return; case preg_match('/boolean/i', $this->_sqlType): $this->_type = 'boolean'; return; } } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Base/ColumnDefinition.php0000664000175000017500000001251112653711335021642 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.3.1/lib/Horde/Db/Adapter/Base/Index.php0000664000175000017500000000500212653711335017440 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.3.1/lib/Horde/Db/Adapter/Base/Result.php0000664000175000017500000001270312653711335017655 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * This class represents the result set of a SELECT query. * * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ abstract class Horde_Db_Adapter_Base_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 = Horde_Db::FETCH_ASSOC; /** * Constructor. * * @param Horde_Db_Adapter $adapter A driver instance. * @param string $sql A SQL 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. */ public function __construct($adapter, $sql, $arg1 = null, $arg2 = null) { $this->_adapter = $adapter; $this->_sql = $sql; $this->_arg1 = $arg1; $this->_arg2 = $arg2; } /** * Destructor. */ 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 array 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 for Iterator. * * @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->_fetchArray(); 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; } /** * 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; } /** * 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; } /** * Sets the default fetch mode for this result. * * @param integer $fetchmode One of the Horde_Db::FETCH_* constants. */ public function setFetchMode($fetchmode) { $this->_fetchMode = $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->_columnCount(); } /** * Returns a row from a resultset. * * @return array|boolean The next row in the resultset or false if there * are no more results. */ abstract protected function _fetchArray(); /** * Returns the number of columns in the result set. * * @return integer Number of columns. */ abstract protected function _columnCount(); } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Base/Schema.php0000664000175000017500000011303312653711335017575 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * Base class for managing database schemes and handling database-specific SQL * dialects and quoting. * * @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' . Horde_String::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. */ 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 $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'; } /** * 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. */ abstract public function nativeDatabaseTypes(); /** * 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); /** * Returns a table column. * * @since Horde_Db 2.1.0 * * @param string $tableName A table name. * @param string $columnName A column name. * * @throws Horde_Db_Exception if column not found. * @return Horde_Db_Adapter_Base_Column A column object. */ public function column($tableName, $columnName) { foreach ($this->columns($tableName) as $column) { if ($column->getName() == $columnName) { return $column; } } throw new Horde_Db_Exception("$tableName does not have a column '$columnName'"); } /** * 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); $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? * * @return string The index name. @since Horde_Db 2.1.0 */ public function addIndex($tableName, $columnName, $options = array()) { $this->_clearTableCache($tableName); $columnNames = (array)$columnName; $indexName = empty($options['name']) ? $this->indexName($tableName, array('column' => $columnNames)) : $this->indexName($tableName, $options); 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)); $this->execute($sql); return $indexName; } /** * 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' || is_array($native) && (isset($native['precision']) || isset($native['scale'])) || isset($precision) || isset($scale)) { $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: * - column: (Horde_Db_Adapter_Base_ColumnDefinition * The column definition class. * - 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->cacheWrite('tables/columns/' . $tableName, ''); $this->cacheWrite('tables/indexes/' . $tableName, ''); } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Base/Table.php0000664000175000017500000001077712653711335017437 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.3.1/lib/Horde/Db/Adapter/Base/TableDefinition.php0000664000175000017500000002271112653711335021437 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) { $this->column($name, '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: * - autoincrement: (boolean) Whether the column is * an autoincrement column. Restrictions are * RDMS specific. * - 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. * - limit: (integer) Maximum column length (string, * text, binary or integer columns only) * - 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). * * @return Horde_Db_Adapter_Base_TableDefinition This object. */ public function column($name, $type, $options = array()) { if ($name == $this->_primaryKey) { throw new LogicException($name . ' has already been added as a primary key'); } $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, $options['limit'], $options['precision'], $options['scale'], $options['unsigned'], $options['default'], $options['null'], $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() { $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.3.1/lib/Horde/Db/Adapter/Mysql/Column.php0000664000175000017500000000452412653711335020071 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 $_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; } } /** */ protected function _setSimplifiedType() { if (strpos(Horde_String::lower($this->_sqlType), 'tinyint(1)') !== false) { $this->_type = 'boolean'; return; } elseif (preg_match('/enum/i', $this->_sqlType)) { $this->_type = 'string'; return; } parent::_setSimplifiedType(); } /** * 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, $this->_hasEmptyStringDefault); } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Mysql/Result.php0000664000175000017500000000336112653711335020110 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * This class represents the result set of a SELECT query from the MySQL * driver. * * @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_Result extends Horde_Db_Adapter_Base_Result { /** * Maps Horde_Db fetch mode constant to the extension constants. * * @var array */ protected $_map = array( Horde_Db::FETCH_ASSOC => MYSQL_ASSOC, Horde_Db::FETCH_NUM => MYSQL_NUM, Horde_Db::FETCH_BOTH => MYSQL_BOTH ); /** * Returns a row from a resultset. * * @return array|boolean The next row in the resultset or false if there * are no more results. */ protected function _fetchArray() { return mysql_fetch_array( $this->_result, $this->_map[$this->_fetchMode] ); } /** * Returns the number of columns in the result set. * * @return integer Number of columns. */ protected function _columnCount() { return mysql_num_fields($this->_result); } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Mysql/Schema.php0000664000175000017500000005221512653711335020034 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * Class for MySQL-specific managing of database schemes and handling of SQL * dialects and quoting. * * @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)); } /*########################################################################## # 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->cacheRead("tables/columns/$tableName")); if (!$rows) { $rows = $this->selectAll( 'SHOW FIELDS FROM ' . $this->quoteTableName($tableName), $name); $this->cacheWrite("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->cacheRead("tables/indexes/$tableName")); 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->cacheWrite("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->cacheRead("tables/columns/$tableName")); if (!$rows) { $rows = $this->selectAll('SHOW FIELDS FROM ' . $this->quoteTableName($tableName), $name); $this->cacheWrite("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()) { return substr(parent::indexName($tableName, $options), 0, 64); } /** * 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.3.1/lib/Horde/Db/Adapter/Mysqli/Result.php0000664000175000017500000000331112653711335020254 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * This class represents the result set of a SELECT query from the MySQLi * driver. * * @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_Mysqli_Result extends Horde_Db_Adapter_Base_Result { /** * Maps Horde_Db fetch mode constant to the extension constants. * * @var array */ protected $_map = array( Horde_Db::FETCH_ASSOC => MYSQLI_ASSOC, Horde_Db::FETCH_NUM => MYSQLI_NUM, Horde_Db::FETCH_BOTH => MYSQLI_BOTH ); /** * Returns a row from a resultset. * * @return array|boolean The next row in the resultset or false if there * are no more results. */ protected function _fetchArray() { return $this->_result->fetch_array($this->_map[$this->_fetchMode]); } /** * Returns the number of columns in the result set. * * @return integer Number of columns. */ protected function _columnCount() { return $this->_result->field_count; } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Oracle/Column.php0000664000175000017500000000575112653711335020174 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @since Horde_Db 2.1.0 * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Oracle_Column extends Horde_Db_Adapter_Base_Column { /*########################################################################## # Construct/Destruct ##########################################################################*/ /** * Constructor. * * @param string $name Column name, such as "supplier_id" in * "supplier_id int(11)". * @param string $default Type-casted default value, such as "new" * in "sales_stage varchar(20) default 'new'". * @param string $sqlType Column type. * @param boolean $null Whether this column allows NULL values. * @param integer $length Column width. * @param integer $precision Precision for NUMBER and FLOAT columns. * @param integer $scale Number of digits to the right of the decimal * point in a number. */ public function __construct($name, $default, $sqlType = null, $null = true, $length = null, $precision = null, $scale = null) { $this->_name = $name; $this->_sqlType = Horde_String::lower($sqlType); $this->_null = $null; $this->_limit = $length; $this->_precision = $precision; $this->_scale = $scale; $this->_setSimplifiedType(); $this->_isText = $this->_type == 'text' || $this->_type == 'string'; $this->_isNumber = $this->_type == 'float' || $this->_type == 'integer' || $this->_type == 'decimal'; $this->_default = $this->typeCast($default); } /*########################################################################## # Type Juggling ##########################################################################*/ /** * Used to convert from BLOBs to Strings * * @return string */ public function binaryToString($value) { if (is_a($value, 'OCI-Lob')) { return $value->load(); } return parent::binaryToString($value); } /*########################################################################## # Protected ##########################################################################*/ /** */ protected function _setSimplifiedType() { if (Horde_String::lower($this->_sqlType) == 'number' && $this->_precision == 1) { $this->_type = 'boolean'; return; } parent::_setSimplifiedType(); } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Oracle/Result.php0000664000175000017500000000263712653711335020215 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @since Horde_Db 2.1.0 * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Oracle_Result extends Horde_Db_Adapter_Base_Result { /** * Maps Horde_Db fetch mode constant to the extension constants. * * @var array */ protected $_map = array( Horde_Db::FETCH_ASSOC => OCI_ASSOC, Horde_Db::FETCH_NUM => OCI_NUM, Horde_Db::FETCH_BOTH => OCI_BOTH ); /** * Returns a row from a resultset. * * @return array|boolean The next row in the resultset or false if there * are no more results. */ protected function _fetchArray() { $array = oci_fetch_array($this->_result, $this->_map[$this->_fetchMode]); if ($array) { $array = array_change_key_case($array, CASE_LOWER); } return $array; } /** * Returns the number of columns in the result set. * * @return integer Number of columns. */ protected function _columnCount() { return oci_num_fields($this->_result); } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Oracle/Schema.php0000664000175000017500000010026112653711335020127 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * Class for Oracle-specific managing of database schemes and handling of SQL * dialects and quoting. * * @since Horde_Db 2.1.0 * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Oracle_Schema extends Horde_Db_Adapter_Base_Schema { /*########################################################################## # Object factories ##########################################################################*/ /** * Factory for Column objects. * * @param string $name Column name, such as "supplier_id" in * "supplier_id int(11)". * @param string $default Type-casted default value, such as "new" * in "sales_stage varchar(20) default 'new'". * @param string $sqlType Column type. * @param boolean $null Whether this column allows NULL values. * @param integer $length Column width. * @param integer $precision Precision for NUMBER and FLOAT columns. * @param integer $scale Number of digits to the right of the decimal * point in a number. * * @return Horde_Db_Adapter_Base_Column A column object. */ public function makeColumn($name, $default, $sqlType = null, $null = true, $length = null, $precision = null, $scale = null) { return new Horde_Db_Adapter_Oracle_Column( $name, $default, $sqlType, $null, $length, $precision, $scale ); } /** * 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_Oracle_TableDefinition($name, $base, $options); } /*########################################################################## # Quoting ##########################################################################*/ /** * Returns a quoted form of the column name. * * With Oracle, if using quoted identifiers, you need to use them * everywhere. 'SELECT * FROM "tablename"' is NOT the same as 'SELECT * * FROM tablename'. Thus we cannot blindly quote table or column names, * unless we know that in subsequent queries they will be used too. * * @param string $name A column name. * * @return string The quoted column name. */ public function quoteColumnName($name) { return $name; } /** * Returns a quoted binary value. * * @param mixed A binary value. * * @return string The quoted binary value. */ public function quoteBinary($value) { return "'" . bin2hex($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' => array('name' => 'number NOT NULL PRIMARY KEY', 'limit' => null, 'null' => null), 'string' => array('name' => 'varchar2', 'limit' => 255), 'text' => array('name' => 'clob', 'limit' => null), 'mediumtext' => array('name' => 'clob', 'limit' => null), 'longtext' => array('name' => 'clob', 'limit' => null), 'integer' => array('name' => 'number', 'limit' => null), 'bigint' => array('name' => 'number', 'limit' => null), 'float' => array('name' => 'float', 'limit' => null), 'decimal' => array('name' => 'number', 'limit' => null), 'datetime' => array('name' => 'date', 'limit' => null), 'timestamp' => array('name' => 'date', 'limit' => null), 'time' => array('name' => 'varchar2', 'limit' => 8), 'date' => array('name' => 'date', 'limit' => null), 'binary' => array('name' => 'blob', 'limit' => null), 'boolean' => array('name' => 'number', 'precision' => 1, 'scale' => 0), ); } /** * Returns the maximum length a table alias can have. * * @return integer The maximum table alias length. */ public function tableAliasLength() { return 30; } /** * 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) { return parent::tableAliasFor($this->_truncate($tableName)); } /** * Returns a list of all tables of the current database. * * @return array A table list. */ public function tables() { return array_map( array('Horde_String', 'lower'), $this->selectValues('SELECT table_name FROM USER_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) { $pk = $this->makeIndex( $tableName, 'PRIMARY', true, true, array() ); $rows = @unserialize($this->cacheRead("tables/primarykeys/$tableName")); if (!$rows) { $constraint = $this->selectOne( 'SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME = ? AND CONSTRAINT_TYPE = \'P\'', array(Horde_String::upper($tableName)), $name ); if ($constraint['constraint_name']) { $pk->name = $constraint['constraint_name']; $rows = $this->selectValues( 'SELECT DISTINCT COLUMN_NAME FROM USER_CONS_COLUMNS WHERE CONSTRAINT_NAME = ?', array($constraint['constraint_name']) ); $rows = array_map(array('Horde_String', 'lower'), $rows); $this->cacheWrite("tables/primarykeys/$tableName", serialize($rows)); } else { $rows = array(); } } $pk->columns = $rows; 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) { $rows = @unserialize($this->cacheRead("tables/indexes/$tableName")); if (!$rows) { $rows = $this->selectAll( 'SELECT INDEX_NAME, UNIQUENESS FROM USER_INDEXES WHERE TABLE_NAME = ? AND INDEX_NAME NOT IN (SELECT INDEX_NAME FROM USER_LOBS)', array(Horde_String::upper($tableName)), $name ); $this->cacheWrite("tables/indexes/$tableName", serialize($rows)); } $indexes = array(); $primary = $this->primaryKey($tableName); foreach ($rows as $row) { if ($row['index_name'] == $primary->name) { continue; } $columns = $this->selectValues( 'SELECT DISTINCT COLUMN_NAME FROM USER_IND_COLUMNS WHERE INDEX_NAME = ?', array($row['index_name']) ); $indexes[] = $this->makeIndex( $tableName, Horde_String::lower($row['index_name']), false, $row['uniqueness'] == 'UNIQUE', array_map(array('Horde_String', 'lower'), $columns) ); } 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->cacheRead("tables/columns/$tableName")); if (!$rows) { $rows = $this->selectAll( 'SELECT COLUMN_NAME, DATA_DEFAULT, DATA_TYPE, NULLABLE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE FROM USER_TAB_COLUMNS WHERE TABLE_NAME = ?', array(Horde_String::upper($tableName)), $name ); $this->cacheWrite("tables/columns/$tableName", serialize($rows)); } // Create columns from rows. $columns = array(); foreach ($rows as $row) { $column = Horde_String::lower($row['column_name']); $columns[$column] = $this->makeColumn( $column, $row['data_default'], $row['data_type'], $row['nullable'] != 'N', $row['data_length'], $row['data_precision'], $row['data_scale'] ); } 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); return $this->execute( sprintf( 'ALTER TABLE %s RENAME TO %s', $this->quoteTableName($name), $this->quoteTableName($newName) ) ); } /** * Drops a table from the database. * * @param string $name A table name. */ public function dropTable($name) { $this->removeAutoincrementTrigger($name); parent::dropTable($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 = $this->quoteColumnName($columnName) . ' ' . $this->typeToSql( $type, $options['limit'], $options['precision'], $options['scale'], $options['unsigned'] ); $sql = $this->addColumnOptions($sql, $options); $sql = sprintf( 'ALTER TABLE %s ADD (%s)', $this->quoteTableName($tableName), $sql ); $this->execute($sql); if ($type == 'autoincrementKey') { $this->createAutoincrementTrigger($tableName, $columnName); } } /** * 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 COLUMN %s', $this->quoteTableName($tableName), $this->quoteColumnName($columnName)); $this->removeAutoincrementTrigger($tableName, $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. */ public function changeColumn($tableName, $columnName, $type, $options = array()) { $options = array_merge( array( 'limit' => null, 'precision' => null, 'scale' => null, 'unsigned' => null ), $options ); $column = $this->column($tableName, $columnName); $isNull = $column->isNull(); if ($type == 'binary' && $column->getType() == 'binary') { return; } $columnOptions = array( 'limit' => $column->getLimit(), 'default' => $column->getDefault(), ); if (!$column->isNull()) { $columnOptions['null'] = false; } $old = $this->addColumnOptions( $this->typeToSql( $column->getType(), $column->getType() == 'integer' || is_null($options['limit']) ? null : $column->getLimit(), is_null($options['precision']) ? null : $column->precision(), is_null($options['scale']) ? null : $column->scale(), is_null($options['unsigned']) ? null : $column->isUnsigned() ), $columnOptions ); $new = $this->typeToSql( $type, $options['limit'], $options['precision'], $options['scale'], $options['unsigned'] ); if ($old == $this->addColumnOptions($new, $options)) { return; } if ($type == 'autoincrementKey') { try { $this->removeAutoincrementTrigger($tableName); $this->removePrimaryKey($tableName); } catch (Horde_Db_Exception $e) { } if (!$isNull) { /* Manually set to NULL, because MODIFY fails if it contains a * NOT NULL constraint and the column already is NOT NULL. */ $sql = $this->quoteColumnName($columnName) . ' ' . $this->typeToSql( $column->getType(), $column->getType() == 'integer' ? null : $column->getLimit(), $column->precision(), $column->scale(), $column->isUnsigned() ); $sql = $this->addColumnOptions($sql, array('null' => true)); $sql = sprintf( 'ALTER TABLE %s MODIFY (%s)', $this->quoteTableName($tableName), $sql ); $this->execute($sql); } } else { /* Jump through some more hoops because MODIFY fails if it contains * a NOT NULL constraint and the column already is NOT NULL. */ if (isset($options['null']) && $isNull == $options['null']) { unset($options['null']); } elseif (!isset($options['null']) && !$isNull) { $options['null'] = true; } } $this->_clearTableCache($tableName); if ($type == 'binary' && $column->getType() != 'binary') { $this->beginDbTransaction(); $this->addColumn($tableName, $columnName . '_tmp', $type, $options); $this->execute(' CREATE OR REPLACE FUNCTION CLOB_TO_BLOB (p_clob CLOB) RETURN BLOB AS l_blob BLOB; l_dest_offset INTEGER := 1; l_source_offset INTEGER := 1; l_lang_context INTEGER := DBMS_LOB.DEFAULT_LANG_CTX; l_warning INTEGER := DBMS_LOB.WARN_INCONVERTIBLE_CHAR; BEGIN DBMS_LOB.CREATETEMPORARY(l_blob, TRUE); DBMS_LOB.CONVERTTOBLOB ( dest_lob => l_blob, src_clob => p_clob, amount => DBMS_LOB.LOBMAXSIZE, dest_offset => l_dest_offset, src_offset => l_source_offset, blob_csid => DBMS_LOB.DEFAULT_CSID, lang_context => l_lang_context, warning => l_warning ); RETURN l_blob; END; '); $this->update(sprintf( 'UPDATE %s SET %s = CLOB_TO_BLOB(%s) WHERE %s IS NOT NULL', $this->quoteTableName($tableName), $this->quoteColumnName($columnName . '_tmp'), $this->quoteColumnName($columnName), $this->quoteColumnName($columnName) )); $this->update(sprintf( 'UPDATE %s SET %s = NULL WHERE %s IS NULL', $this->quoteTableName($tableName), $this->quoteColumnName($columnName . '_tmp'), $this->quoteColumnName($columnName) )); $this->removeColumn($tableName, $columnName); $this->renameColumn($tableName, $columnName . '_tmp', $columnName); $this->commitDbTransaction(); return; } if ($type != 'binary' && $column->getType() == 'binary') { $this->beginDbTransaction(); $this->addColumn($tableName, $columnName . '_tmp', $type, $options); $this->update(sprintf( 'UPDATE %s SET %s = UTL_RAW.CAST_TO_VARCHAR2(%s)', $this->quoteTableName($tableName), $this->quoteColumnName($columnName . '_tmp'), $this->quoteColumnName($columnName) )); $this->removeColumn($tableName, $columnName); $this->renameColumn($tableName, $columnName . '_tmp', $columnName); $this->commitDbTransaction(); return; } if ($type == 'text' && $column->getType() != 'text') { $this->beginDbTransaction(); $this->addColumn($tableName, $columnName . '_tmp', $type, $options); $this->update(sprintf( 'UPDATE %s SET %s = TO_CLOB(%s)', $this->quoteTableName($tableName), $this->quoteColumnName($columnName . '_tmp'), $this->quoteColumnName($columnName) )); $this->removeColumn($tableName, $columnName); $this->renameColumn($tableName, $columnName . '_tmp', $columnName); $this->commitDbTransaction(); return; } if ($type != 'text' && $column->getType() == 'text') { $this->beginDbTransaction(); $this->addColumn($tableName, $columnName . '_tmp', $type, $options); $this->update(sprintf( 'UPDATE %s SET %s = DBMS_LOB.SUBSTR(%s, 4000)', $this->quoteTableName($tableName), $this->quoteColumnName($columnName . '_tmp'), $this->quoteColumnName($columnName) )); $this->removeColumn($tableName, $columnName); $this->renameColumn($tableName, $columnName . '_tmp', $columnName); $this->commitDbTransaction(); return; } $sql = $this->quoteColumnName($columnName) . ' ' . $new; $sql = $this->addColumnOptions($sql, $options); $sql = sprintf( 'ALTER TABLE %s MODIFY (%s)', $this->quoteTableName($tableName), $sql ); $this->execute($sql); if ($type == 'autoincrementKey') { $this->createAutoincrementTrigger($tableName, $columnName); } } /** * Creates sequences and triggers for an autoincrementKey column. * * @since Horde_Db 2.1.0 * * @param string $tableName A table name. * @param string $columnName A column name. */ public function createAutoincrementTrigger($tableName, $columnName) { // Build the table that holds the last autoincremented value. Used for // example for returning the ID from last INSERT. $id = $tableName . '_' . $columnName; if (!$this->selectValue('SELECT 1 FROM USER_TABLES WHERE TABLE_NAME = \'HORDE_DB_AUTOINCREMENT\'')) { $this->execute('CREATE TABLE horde_db_autoincrement (id INTEGER)'); $this->execute('INSERT INTO horde_db_autoincrement (id) VALUES (0)'); } // Create a sequence that automatically increments when queried with // .NEXTVAL. $sequence = $this->_truncate($id . '_seq'); $sql = sprintf( 'CREATE SEQUENCE %s', $sequence ); // See if the column already has values, to start the sequence at a // higher value. $max = $this->selectValue( sprintf( 'SELECT MAX(%s) FROM %s', $this->quoteColumnName($columnName), $tableName ) ); if ($max) { $sql .= ' MINVALUE ' . ($max + 1); } $this->execute($sql); // Create the actual trigger that inserts the next value from the // sequence into the autoincrementKey column when inserting a row. $this->execute(sprintf( 'CREATE OR REPLACE TRIGGER %s BEFORE INSERT ON %s FOR EACH ROW DECLARE increment INTEGER; BEGIN SELECT %s.NEXTVAL INTO :NEW.%s FROM dual; SELECT %s.CURRVAL INTO increment FROM dual; UPDATE horde_db_autoincrement SET id = increment; END;', $this->_truncate($id . '_trig'), $tableName, $sequence, $columnName, $sequence )); } /** * Drops sequences and triggers for an autoincrementKey column. * * If $columnName is specified, the sequences and triggers are only dropped * if $columnName is actually an autoincrementKey column. * * @since Horde_Db 2.1.0 * * @param string $tableName A table name. * @param string $columnName A column name. */ public function removeAutoincrementTrigger($tableName, $columnName = null) { $pk = $this->primaryKey($tableName); if (count($pk->columns) == 1 && (!$columnName || $pk->columns[0] == $columnName)) { $prefix = $tableName . '_' . $pk->columns[0]; try { $this->execute(sprintf( 'DROP SEQUENCE %s', $this->quoteColumnName($this->_truncate($prefix . '_seq')) )); } catch (Horde_Db_Exception $e) { } try { $this->execute(sprintf( 'DROP TRIGGER %s', $this->quoteColumnName($this->_truncate($prefix . '_trig')) )); } catch (Horde_Db_Exception $e) { } } } /** * 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 MODIFY (%s DEFAULT %s)', $this->quoteTableName($tableName), $this->quoteColumnName($columnName), $this->quote($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); $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); $sql = sprintf('ALTER TABLE %s DROP PRIMARY KEY', $this->quoteTableName($tableName)); return $this->execute($sql); } /** * Removes an index from a table. * * @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', $this->quoteColumnName($index)); return $this->execute($sql); } /** * Builds the name for an index. * * Cuts the index name to the maximum length of 30 characters limited by * Oracle. * * @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()) { $index = parent::indexName($tableName, $options); if (strlen($index) <= 30) { return $index; } if (isset($options['name']) && $index == $options['name']) { return $this->_truncate($index); } return substr('ind_' . $this->_truncate($tableName, 15) . '_' . hash('crc32', $index), 0, 30); } /** * Creates a database. * * @param string $name A database name. * @param array $options Database options. */ public function createDatabase($name, $options = array()) { return $this->execute(sprintf('CREATE DATABASE %s', $this->quoteTableName($name))); } /** * Drops a database. * * @param string $name A database name. */ public function dropDatabase($name) { if ($this->currentDatabase() != $name) { throw new Horde_Db_Exception('Oracle can only drop the current database'); } return $this->execute('DROP DATABASE'); } /** * Returns the name of the currently selected database. * * @return string The database name. */ public function currentDatabase() { return $this->selectValue("SELECT SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA') FROM DUAL"); } /** * Adds default/null options to column SQL definitions. * * @param string $sql Existing SQL definition for a column. * @param array $options Column options: * - column: (Horde_Db_Adapter_Base_ColumnDefinition * The column definition class. * - 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['default'])) { $default = $options['default']; $column = isset($options['column']) ? $options['column'] : null; $sql .= ' DEFAULT ' . $this->quote($default, $column); } if (isset($options['null']) && (!isset($options['column']) || ($options['column']->getType() != 'text' && $options['column']->getType() != 'binary'))) { if ($options['null']) { $sql .= ' NULL'; } else { $sql .= ' NOT NULL'; } } 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()) { $lhs = $this->_escapePrepare($lhs); switch ($op) { case '|': if ($bind) { return array($lhs . ' + ? - BITAND(' . $lhs . ', ?)', array((int)$rhs, (int)$rhs)); } return $lhs . ' + ' . (int)$rhs . ' - BITAND(' . $lhs . ', ' . (int)$rhs . ')'; case '&': if ($bind) { return array('BITAND(' . $lhs . ', ?)', array((int)$rhs)); } return 'BITAND(' . $lhs . ', ' . (int)$rhs . ')'; } return parent::buildClause($lhs, $op, $rhs, $bind, $params); } /*########################################################################## # Protected ##########################################################################*/ /** * Clears the cache for tables when altering them. * * @param string $tableName A table name. */ protected function _clearTableCache($tableName) { parent::_clearTableCache($tableName); $this->cacheWrite('tables/primarykeys/' . $tableName, ''); } /** * Truncates an indentifier to a certain length. * * To avoid collisions, the identifier is split up by underscores and the * parts truncated to 3 characters first. * * @param string $name An identifier. * @param integer $length The maximum length. * * @return string The truncated identifier. */ protected function _truncate($name, $length = 30) { if (strlen($name) > $length) { $name = implode( '_', array_map( function($t) { return substr($t, 0, 3); }, explode('_', $name) ) ); } return substr($name, 0, $length); } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Oracle/TableDefinition.php0000664000175000017500000000512012653711335021765 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @since Horde_Db 2.1.0 * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Oracle_TableDefinition extends Horde_Db_Adapter_Base_TableDefinition { protected $_createTrigger = false; /** * Adds a new column to the table definition. * * @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()) { parent::column($name, $type, $options); if ($type == 'autoincrementKey') { $this->_createTrigger = $name; } return $this; } /** * Wrap up table creation block & create the table */ public function end() { parent::end(); if ($this->_createTrigger) { $this->_base->createAutoincrementTrigger($this->_name, $this->_createTrigger); } } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Pdo/Base.php0000664000175000017500000003051712653711335017124 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. PDOException: ' . $e->getMessage(); $this->_logError($msg, ''); $e2 = new Horde_Db_Exception($msg); $e2->logged = true; throw $e2; } $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'; try { return isset($this->_connection) && $this->_connection->query($sql); } catch (PDOException $e) { throw new Horde_Db_Exception($e); } } /*########################################################################## # 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 Horde_Db_Adapter_Pdo_Result * @throws Horde_Db_Exception */ public function select($sql, $arg1 = null, $arg2 = null) { return new Horde_Db_Adapter_Pdo_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) { $stmt = $this->execute($sql, $arg1, $arg2); if (!$stmt) { return array(); } $result = $stmt->fetchAll(PDO::FETCH_ASSOC); // Required to really close the connection. $stmt = null; return $result; } /** * 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) { $stmt = $this->execute($sql, $arg1, $arg2); if (!$stmt) { return array(); } $result = $stmt->fetch(PDO::FETCH_ASSOC); // Required to really close the connection. $stmt = null; return $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) { $stmt = $this->execute($sql, $arg1, $arg2); if (!$stmt) { return null; } $result = $stmt->fetchColumn(0); // Required to really close the connection. $stmt = null; 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 * @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) { $stmt = $this->execute($sql, $arg1, $arg2); if (!$stmt) { return null; } $result = $stmt->fetchAll(PDO::FETCH_COLUMN, 0); // Required to really close the connection. $stmt = null; 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 * @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) { $stmt = $this->execute($sql, $arg1, $arg2); if (!$stmt) { return null; } $result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); // Required to really close the connection. $stmt = null; return $result; } /** * Executes the SQL statement in the context of this connection. * * @deprecated Deprecated for external usage. Use select() instead. * * @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 (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 (PDOException $e) { $this->_logInfo($sql, $name); $this->_logError($sql, 'QUERY FAILED: ' . $e->getMessage()); 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); try { return $idValue ? $idValue : $this->_connection->lastInsertId($sequenceName); } catch (PDOException $e) { throw new Horde_Db_Exception($e); } } /** * Begins the transaction (and turns off auto-committing). */ public function beginDbTransaction() { if (!$this->_transactionStarted) { try { $this->_connection->beginTransaction(); } catch (PDOException $e) { throw new Horde_Db_Exception($e); } } $this->_transactionStarted++; } /** * Commits the transaction (and turns on auto-committing). */ public function commitDbTransaction() { $this->_transactionStarted--; if (!$this->_transactionStarted) { try { $this->_connection->commit(); } catch (PDOException $e) { throw new Horde_Db_Exception($e); } } } /** * 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; } try { $this->_connection->rollBack(); } catch (PDOException $e) { throw new Horde_Db_Exception($e); } $this->_transactionStarted = 0; } /*########################################################################## # Quoting ##########################################################################*/ /** * Quotes a string, escaping any ' (single quote) and \ (backslash) * characters.. * * @param string $string * @return string */ public function quoteString($string) { try { return $this->_connection->quote($string); } catch (PDOException $e) { throw new Horde_Db_Exception($e); } } /*########################################################################## # 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.3.1/lib/Horde/Db/Adapter/Pdo/Mysql.php0000664000175000017500000000743512653711335017362 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.3.1/lib/Horde/Db/Adapter/Pdo/Pgsql.php0000664000175000017500000002165512653711335017343 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 true; } /*########################################################################## # 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]); } $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.3.1/lib/Horde/Db/Adapter/Pdo/Result.php0000664000175000017500000000307612653711335017530 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * This class represents the result set of a SELECT query from the PDO drivers. * * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Pdo_Result extends Horde_Db_Adapter_Base_Result { /** * Maps Horde_Db fetch mode constant to the extension constants. * * @var array */ protected $_map = array( Horde_Db::FETCH_ASSOC => PDO::FETCH_ASSOC, Horde_Db::FETCH_NUM => PDO::FETCH_NUM, Horde_Db::FETCH_BOTH => PDO::FETCH_BOTH ); /** * Returns a row from a resultset. * * @return array|boolean The next row in the resultset or false if there * are no more results. */ protected function _fetchArray() { try { return $this->_result->fetch($this->_map[$this->_fetchMode]); } catch (PDOException $e) { throw new Horde_Db_Exception($e); } } /** * Returns the number of columns in the result set. * * @return integer Number of columns. */ protected function _columnCount() { try { return $this->_result->columnCount(); } catch (PDOException $e) { throw new Horde_Db_Exception($e); } } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Pdo/Sqlite.php0000664000175000017500000001400612653711335017506 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'; } /** * Does this adapter support using INTERVAL statements? This is +true+ * for all adapters except sqlite. * * @return boolean */ public function supportsInterval() { return false; } 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.3.1/lib/Horde/Db/Adapter/Postgresql/Column.php0000664000175000017500000001706112653711335021127 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; /** * 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); } /** */ protected function _setSimplifiedType() { switch (true) { case preg_match('/^(?:real|double precision)$/', $this->_sqlType): // Numeric and monetary types $this->_type = 'float'; return; case preg_match('/^money$/', $this->_sqlType): // Monetary types $this->_type = 'decimal'; return; case preg_match('/^(?:character varying|bpchar)(?:\(\d+\))?$/', $this->_sqlType): // Character types $this->_type = 'string'; return; case preg_match('/^bytea$/', $this->_sqlType): // Binary data types $this->_type = 'binary'; return; case preg_match('/^timestamp with(?:out)? time zone$/', $this->_sqlType): // Date/time types $this->_type = 'datetime'; return; case preg_match('/^interval$/', $this->_sqlType): $this->_type = 'string'; return; case preg_match('/^(?:point|line|lseg|box|"?path"?|polygon|circle)$/', $this->_sqlType): // Geometric types $this->_type = 'string'; return; case preg_match('/^(?:cidr|inet|macaddr)$/', $this->_sqlType): // Network address types $this->_type = 'string'; return; case preg_match('/^bit(?: varying)?(?:\(\d+\))?$/', $this->_sqlType): // Bit strings $this->_type = 'string'; return; case preg_match('/^xml$/', $this->_sqlType): // XML type $this->_type = 'string'; return; case preg_match('/^\D+\[\]$/', $this->_sqlType): // Arrays $this->_type = 'string'; return; case preg_match('/^oid$/', $this->_sqlType): // Object identifier types $this->_type = 'integer'; return; } // Pass through all types that are not specific to PostgreSQL. parent::_setSimplifiedType(); } /** * Extracts the value from a PostgreSQL column default definition. */ protected function _extractValueFromDefault($default) { switch (true) { case preg_match('/\A-?\d+(\.\d*)?\z/', $default): // Numeric types return $default; case preg_match('/\A\'(.*)\'::(?:character varying|bpchar|text)\z/m', $default, $matches): // Character types return $matches[1]; case preg_match('/\AE\'(.*)\'::(?:character varying|bpchar|text)\z/m', $default, $matches): // Character types (8.1 formatting) /*@TODO fix preg callback*/ return preg_replace('/\\(\d\d\d)/', '$1.oct.chr', $matches[1]); case preg_match('/\A\'(.*)\'::bytea\z/m', $default, $matches): // Binary data types return $matches[1]; case preg_match('/\A\'(.+)\'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/', $default, $matches): // Date/time types return $matches[1]; case preg_match('/\A\'(.*)\'::interval\z/', $default, $matches): return $matches[1]; case $default == 'true': // Boolean type return true; case $default == 'false': return false; case preg_match('/\A\'(.*)\'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/', $default, $matches): // Geometric types return $matches[1]; case preg_match('/\A\'(.*)\'::(?:cidr|inet|macaddr)\z/', $default, $matches): // Network address types return $matches[1]; case preg_match('/\AB\'(.*)\'::"?bit(?: varying)?"?\z/', $default, $matches): // Bit string types return $matches[1]; case preg_match('/\A\'(.*)\'::xml\z/m', $default, $matches): // XML type return $matches[1]; case preg_match('/\A\'(.*)\'::"?\D+"?\[\]\z/', $default, $matches): // Arrays return $matches[1]; case preg_match('/\A-?\d+\z/', $default, $matches): // Object identifier types 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.3.1/lib/Horde/Db/Adapter/Postgresql/Schema.php0000664000175000017500000012262112653711335021071 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * Class for PostgreSQL-specific managing of database schemes and handling of * SQL dialects and quoting. * * @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('/^[0-9A-F]*$/i')) { // Hexadecimal notation return "X'" . $value . "'"; } if (preg_match('/^[01]*$/', $value)) { // Bit-string notation return "B'" . $value . "'"; } } return parent::quote($value, $column); } /** * 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 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 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. * * @return integer The maximum table alias length. */ public function tableAliasLength() { return (int)$this->selectValue('SHOW max_identifier_length'); } /** * Returns a list of all tables in the schema search path. * * @return array A table list. */ public function tables() { return $this->selectValues('SELECT table_name FROM information_schema.tables WHERE table_schema = ANY (CURRENT_SCHEMAS(false));'); } /** * 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->cacheRead("tables/indexes/$tableName")); if (!$indexes) { $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 = ANY(CURRENT_SCHEMAS(false))) 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->cacheWrite("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->cacheRead("tables/columns/$tableName")); if (!$rows) { $rows = $this->_columnDefinitions($tableName, $name); $this->cacheWrite("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 = $this->column($tableName, $columnName)->getType(); $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); $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) { return $this->execute('DROP DATABASE IF EXISTS ' . $this->quoteTableName($name)); } /** * 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) ELSE 0 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 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.3.1/lib/Horde/Db/Adapter/Sqlite/Column.php0000664000175000017500000000436612653711335020231 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 { 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.3.1/lib/Horde/Db/Adapter/Sqlite/Schema.php0000664000175000017500000006416312653711335020175 0ustar janjan * @author Derek DeVries * @author Chuck Hagenbuch * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * Class for SQLite-specific managing of database schemes and handling of SQL * dialects and quoting. * * @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 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->cacheRead("tables/columns/$tableName")); if (!$rows) { $rows = $this->selectAll('PRAGMA table_info(' . $this->quoteTableName($tableName) . ')', $name); $this->cacheWrite("tables/columns/$tableName", serialize($rows)); } $pk = $this->makeIndex($tableName, 'PRIMARY', true, true, array()); foreach ($rows as $row) { if ($row['pk']) { $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->cacheRead("tables/indexes/$tableName")); 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->cacheWrite("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->cacheRead("tables/columns/$tableName")); if (!$rows) { $rows = $this->selectAll('PRAGMA table_info(' . $this->quoteTableName($tableName) . ')', $name); $this->cacheWrite("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()) { if ($this->transactionStarted()) { throw new Horde_Db_Exception('Cannot add columns to a SQLite database while inside a transaction'); } if ($type == 'autoincrementKey') { $this->_alterTable( $tableName, array(), create_function( '$definition', sprintf( '$definition->column("%s", "%s", %s);', $columnName, $type, var_export($options, true) ) ) ); } else { 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");', $columnName, $type), sprintf('if ("%s" == "autoincrementKey") $definition->primaryKey(false);', $type) ); if (isset($options['limit'])) { $defs[] = sprintf('$definition["%s"]->setLimit("%s");', $columnName, $options['limit']); } if (isset($options['null'])) { $defs[] = sprintf('$definition["%s"]->setNull((bool)%d);', $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(); $columnOptions = array('limit' => $column->getLimit()); if ($columnType == 'autoincrementKey') { $copyPk = false; } else { $columnOptions['default'] = $column->getDefault(); $columnOptions['null'] = $column->isNull(); } $definition->column($columnName, $columnType, $columnOptions); } 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.3.1/lib/Horde/Db/Adapter/Base.php0000664000175000017500000005465212653711335016410 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; /** * Cache prefix. * * @var string */ protected $_cachePrefix; /** * 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 * * @param Horde_Cache $cache 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; } /** * Does this adapter support using INTERVAL statements? This is +true+ * for all adapters except sqlite. * * @return boolean */ public function supportsInterval() { 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. * * @deprecated * @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; } /** * Writes values to the cache handler. * * The key is automatically prefixed to avoid collisions when using * different adapters or different configurations. * * @since Horde_Db 2.1.0 * * @param string $key A cache key. * @param string $value A value. */ public function cacheWrite($key, $value) { $this->_cache->set($this->_cacheKey($key), $value); } /** * Reads values from the cache handler. * * The key is automatically prefixed to avoid collisions when using * different adapters or different configurations. * * @since Horde_Db 2.1.0 * * @param string $key A cache key. * * @return string A value. */ public function cacheRead($key) { return $this->_cache->get($this->_cacheKey($key), 0); } /*########################################################################## # 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 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; } /** * Inserts a row including BLOBs into a table. * * @since Horde_Db 2.1.0 * * @param string $table The table name. * @param array $fields A hash of column names and values. BLOB columns * must be provided as Horde_Db_Value_Binary * objects. * @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. * * @return integer Last inserted ID. * @throws Horde_Db_Exception */ public function insertBlob($table, $fields, $pk = null, $idValue = null) { $query = sprintf( 'INSERT INTO %s (%s) VALUES (%s)', $this->quoteTableName($table), implode(', ', array_map(array($this, 'quoteColumnName'), array_keys($fields))), implode(', ', array_fill(0, count($fields), '?')) ); return $this->insert($query, $fields, null, $pk, $idValue); } /** * 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; } /** * Updates rows including BLOBs into a table. * * @since Horde_Db 2.2.0 * * @param string $table The table name. * @param array $fields A hash of column names and values. BLOB * columns must be provided as * Horde_Db_Value_Binary objects. * @param string|array $where A WHERE clause. Either a complete clause or * an array containing a clause with * placeholders and a list of values. * * @throws Horde_Db_Exception */ public function updateBlob($table, $fields, $where = null) { if (is_array($where)) { $where = $this->_replaceParameters($where[0], $where[1]); } $fnames = array(); foreach (array_keys($fields) as $field) { $fnames[] = $this->quoteColumnName($field) . ' = ?'; } $query = sprintf( 'UPDATE %s SET %s%s', $this->quoteTableName($table), implode(', ', $fnames), strlen($where) ? ' WHERE ' . $where : '' ); return $this->update($query, $fields); } /** * 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; } /** * 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); } /** * Returns the prefixed cache key to use. * * @param string $key A cache key. * * @return string Prefixed cache key. */ protected function _cacheKey($key) { if (!isset($this->_cachePrefix)) { $this->_cachePrefix = get_class($this) . hash((version_compare(PHP_VERSION, '5.4', '>=')) ? 'fnv132' : 'sha1', serialize($this->_config)); } return $this->_cachePrefix . $key; } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/Mysql.php0000664000175000017500000002776112653711335016644 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 { /** * 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); } parent::disconnect(); } /** * 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 Horde_Db_Adapter_Mysql_Result */ 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. * * @deprecated Deprecated for external usage. Use select() instead. * * @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 resource * @throws Horde_Db_Exception */ 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, $name); $this->_logError($sql, 'QUERY FAILED: ' . mysql_error($this->_connection)); 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.3.1/lib/Horde/Db/Adapter/Mysqli.php0000664000175000017500000003364312653711335017011 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 { /** * 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(); } parent::disconnect(); } /** * 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 Horde_Db_Adapter_Mysqli_Result */ 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. * * @deprecated Deprecated for external usage. Use select() instead. * * @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 mysqli_result * @throws Horde_Db_Exception */ 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, $name); $this->_logError($sql, 'QUERY FAILED: ' . $this->_connection->error); 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() { $this->_transactionStarted--; if (!$this->_transactionStarted) { $this->_connection->commit(); $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() { if (!$this->_transactionStarted) { return; } $this->_connection->rollback(); $this->_transactionStarted = 0; $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.3.1/lib/Horde/Db/Adapter/Oci8.php0000664000175000017500000004743312653711335016337 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ /** * @since Horde_Db 2.1.0 * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage Adapter */ class Horde_Db_Adapter_Oci8 extends Horde_Db_Adapter_Base { /** * Schema class to use. * * @var string */ protected $_schemaClass = 'Horde_Db_Adapter_Oracle_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 'Oracle'; } /** * Does this adapter support migrations? * * @return boolean */ public function supportsMigrations() { return true; } /*######################################################################### # Connection Management #########################################################################*/ /** * Connect to the db */ public function connect() { if ($this->_active) { return; } $this->_checkRequiredConfig(array('username')); if (!isset($this->_config['tns']) && empty($this->_config['host'])) { throw new Horde_Db_Exception('Either a TNS name or a host name must be specified'); } if (isset($this->_config['tns'])) { $connection = $this->_config['tns']; } else { $connection = $this->_config['host']; if (!empty($this->_config['port'])) { $connection .= ':' . $this->_config['port']; } if (!empty($this->_config['service'])) { $connection .= '/' . $this->_config['service']; } if (!empty($this->_config['type'])) { $connection .= ':' . $this->_config['type']; } if (!empty($this->_config['instance'])) { $connection .= '/' . $this->_config['instance']; } } $oci = oci_connect( $this->_config['username'], isset($this->_config['password']) ? $this->_config['password'] : '', $connection, $this->_oracleCharsetName($this->_config['charset']) ); if (!$oci) { if ($error = oci_error()) { throw new Horde_Db_Exception( sprintf( 'Connect failed: (%d) %s', $error['code'], $error['message'] ), $error['code'] ); } else { throw new Horde_Db_Exception('Connect failed'); } } $this->_connection = $oci; $this->_active = true; $this->execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'"); } /** * Disconnect from db */ public function disconnect() { if ($this->_connection) { oci_close($this->_connection); } parent::disconnect(); } /*######################################################################### # Quoting #########################################################################*/ /** * Quotes a string, escaping any special characters. * * @param string $string * @return string */ public function quoteString($string) { return "'" . str_replace("'", "''", $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 Horde_Db_Adapter_Oracle_Result * @throws Horde_Db_Exception */ public function select($sql, $arg1 = null, $arg2 = null) { return new Horde_Db_Adapter_Oracle_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 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) { $stmt = $this->execute($sql, $arg1, $arg2); $result = oci_fetch_all($stmt, $rows, 0, -1, OCI_FETCHSTATEMENT_BY_ROW); if ($result === false) { $this->_handleError($stmt, 'selectAll'); } foreach ($rows as &$row) { $row = array_change_key_case($row, CASE_LOWER); } 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) { if ($row = oci_fetch_assoc($this->execute($sql, $arg1, $arg2))) { return array_change_key_case( $row, CASE_LOWER ); } } /** * 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) { $stmt = $this->execute($sql, $arg1, $arg2); if (!oci_fetch($stmt)) { return; } if (($result = oci_result($stmt, 1)) === false) { $this->_handleError($stmt, 'selectValue'); } 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) { $stmt = $this->execute($sql, $arg1, $arg2); $values = array(); while (oci_fetch($stmt)) { if (($result = oci_result($stmt, 1)) === false) { $this->_handleError($stmt, 'selectValues'); } $values[] = $result; } 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 resource * @throws Horde_Db_Exception */ public function execute($sql, $arg1 = null, $arg2 = null, $lobs = array()) { 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 = @oci_parse($this->_connection, $sql); $descriptors = array(); foreach ($lobs as $name => $lob) { $descriptors[$name] = oci_new_descriptor($this->_connection, OCI_DTYPE_LOB); oci_bind_by_name($stmt, ':' . $name, $descriptors[$name], -1, $lob instanceof Horde_Db_Value_Binary ? OCI_B_BLOB : OCI_B_CLOB); } $flags = $lobs ? OCI_DEFAULT : ($this->_transactionStarted ? OCI_NO_AUTO_COMMIT : OCI_COMMIT_ON_SUCCESS ); if (!$stmt || !@oci_execute($stmt, $flags)) { $error = oci_error($stmt ?: $this->_connection); if ($stmt) { oci_free_statement($stmt); } $this->_logInfo($sql, $name); $this->_logError($sql, 'QUERY FAILED: ' . $error['message']); throw new Horde_Db_Exception( $this->_errorMessage($error), $error['code'] ); } foreach ($lobs as $name => $lob) { $descriptors[$name]->save($lob->value); } if ($lobs) { oci_commit($this->_connection); } $this->_logInfo($sql, $name, $t->pop()); $this->_rowCount = oci_num_rows($stmt); 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->selectValue('SELECT id FROM horde_db_autoincrement'); } /** * Inserts a row including BLOBs into a table. * * @since Horde_Db 2.1.0 * * @param string $table The table name. * @param array $fields A hash of column names and values. BLOB columns * must be provided as Horde_Db_Value_Binary * objects. * @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. * * @return integer Last inserted ID. * @throws Horde_Db_Exception */ public function insertBlob($table, $fields, $pk = null, $idValue = null) { list($fields, $blobs, $locators) = $this->_prepareBlobs($fields); $sql = 'INSERT INTO ' . $this->quoteTableName($table) . ' (' . implode( ', ', array_map(array($this, 'quoteColumnName'), array_keys($fields)) ) . ') VALUES (' . implode(', ', $fields) . ')'; // Protect against empty values being passed for blobs. if (!empty($blobs)) { $sql .= ' RETURNING ' . implode(', ', array_keys($blobs)) . ' INTO ' . implode(', ', $locators); } $this->execute($sql, null, null, $blobs); return $idValue ? $idValue : $this->selectValue('SELECT id FROM horde_db_autoincrement'); } /** * Updates rows including BLOBs into a table. * * @since Horde_Db 2.2.0 * * @param string $table The table name. * @param array $fields A hash of column names and values. BLOB * columns must be provided as * Horde_Db_Value_Binary objects. * @param string|array $where A WHERE clause. Either a complete clause or * an array containing a clause with * placeholders and a list of values. * * @throws Horde_Db_Exception */ public function updateBlob($table, $fields, $where = null) { list($fields, $blobs, $locators) = $this->_prepareBlobs($fields); if (is_array($where)) { $where = $this->_replaceParameters($where[0], $where[1]); } $fnames = array(); foreach ($fields as $field => $value) { $fnames[] = $this->quoteColumnName($field) . ' = ' . $value; } $sql = sprintf( 'UPDATE %s SET %s%s', $this->quoteTableName($table), implode(', ', $fnames), strlen($where) ? ' WHERE ' . $where : '' ); // Protect against empty values for blobs. if (!empty($blobs)) { $sql .= sprintf(' RETURNING %s INTO %s', implode(', ', array_keys($blobs)), implode(', ', $locators) ); } $this->execute($sql, null, null, $blobs); return $this->_rowCount; } /** * Prepares a list of field values to be consumed by insertBlob() or * updateBlob(). * * @param array $fields A hash of column names and values. BLOB columns * must be provided as Horde_Db_Value_Binary objects. * * @return array A list of fields, blobs, and locators. */ protected function _prepareBlobs($fields) { $blobs = $locators = array(); foreach ($fields as $column => &$field) { if ($field instanceof Horde_Db_Value_Binary || $field instanceof Horde_Db_Value_Text) { $blobs[$this->quoteColumnName($column)] = $field; $locators[] = ':' . $this->quoteColumnName($column); $field = $field instanceof Horde_Db_Value_Binary ? 'EMPTY_BLOB()' : 'EMPTY_CLOB()'; } else { $field = $this->quote($field); } } return array($fields, $blobs, $locators); } /** * Begins the transaction (and turns off auto-committing). */ public function beginDbTransaction() { $this->_transactionStarted++; } /** * Commits the transaction (and turns on auto-committing). */ public function commitDbTransaction() { $this->_transactionStarted--; if (!$this->_transactionStarted) { if (!oci_commit($this->_connection)) { $this->_handleError($this->_connection, '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() { if (!$this->_transactionStarted) { return; } $this->_transactionStarted = 0; if (!oci_rollback($this->_connection)) { $this->_handleError($this->_connection, 'rollbackDbTransaction'); } } /** * 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'])) { $offset = isset($options['offset']) ? $options['offset'] : 0; $limit = $options['limit'] + $offset; if ($limit) { $sql = "SELECT a.*, ROWNUM rnum FROM ($sql) a WHERE ROWNUM <= $limit"; if ($offset) { $sql = "SELECT * FROM ($sql) WHERE rnum > $offset"; } } } return $sql; } /*######################################################################### # Protected #########################################################################*/ /** * Returns the Oracle name of a character set. * * @param string $charset A charset name. * * @return string Oracle-normalized charset. */ public function _oracleCharsetName($charset) { return str_replace( array( 'iso-8859-1', 'iso-8859-2', 'iso-8859-4', 'iso-8859-5', 'iso-8859-6', 'iso-8859-7', 'iso-8859-8', 'iso-8859-9', 'iso-8859-1', 'iso-8859-10', 'iso-8859-13', 'iso-8859-15', 'shift_jis', 'shift-jis', 'windows-949', 'windows-950', 'windows-1250', 'windows-1251', 'windows-1252', 'windows-1253', 'windows-1254', 'windows-1255', 'windows-1256', 'windows-1257', 'windows-1258', 'utf-8', ), array( 'WE8ISO8859P1', 'EE8ISO8859P2', 'NEE8ISO8859P4', 'CL8ISO8859P5', 'AR8ISO8859P6', 'EL8ISO8859P7', 'IW8ISO8859P8', 'WE8ISO8859P9', 'NE8ISO8859P10', 'BLT8ISO8859P13', 'WE8ISO8859P15', 'JA16SJIS', 'JA16SJIS', 'KO16MSWIN949', 'ZHT16MSWIN950', 'EE8MSWIN1250', 'CL8MSWIN1251', 'WE8MSWIN1252', 'EL8MSWIN1253', 'TR8MSWIN1254', 'IW8MSWIN1255', 'AR8MSWIN1256', 'BLT8MSWIN1257', 'VN8MSWIN1258', 'AL32UTF8', ), Horde_String::lower($charset) ); } /** * Creates a formatted error message from a oci_error() result hash. * * @param array $error Hash returned from oci_error(). * * @return string The formatted error message. */ protected function _errorMessage($error) { return 'QUERY FAILED: ' . $error['message'] . "\n\nat offset " . $error['offset'] . "\n" . $error['sqltext']; } /** * Log and throws an exception for the last error. * * @param resource $resource The resource (connection or statement) to * call oci_error() upon. * @param string $method The calling method. * * @throws Horde_Db_Exception */ protected function _handleError($resource, $method) { $error = oci_error($resource); $this->_logError( $error['message'], 'Horde_Db_Adapter_Oci8::' . $method. '()' ); throw new Horde_Db_Exception( $this->_errorMessage($error), $error['code'] ); } } Horde_Db-2.3.1/lib/Horde/Db/Adapter/SplitRead.php0000664000175000017500000004054312653711335017417 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; } /** * Inserts a row including BLOBs into a table. * * @since Horde_Db 2.1.0 * * @param string $table The table name. * @param array $fields A hash of column names and values. BLOB columns * must be provided as Horde_Db_Value_Binary * objects. * @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. * * @return integer Last inserted ID. * @throws Horde_Db_Exception */ public function insertBlob($table, $fields, $pk = null, $idValue = null) { $result = $this->_write->insertBlob($table, $fields, $pk, $idValue); $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; } /** * Updates rows including BLOBs into a table. * * @since Horde_Db 2.2.0 * * @param string $table The table name. * @param array $fields A hash of column names and values. BLOB columns * must be provided as Horde_Db_Value_Binary objects. * @param string $where A WHERE clause. * * @throws Horde_Db_Exception */ public function updateBlob($table, $fields, $where = '') { $result = $this->_write->updateBlob($table, $fields, $where); $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.3.1/lib/Horde/Db/Migration/Base.php0000664000175000017500000001053012653711335016744 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.3.1/lib/Horde/Db/Migration/Exception.php0000664000175000017500000000133612653711335020034 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.3.1/lib/Horde/Db/Migration/Migrator.php0000664000175000017500000002303712653711335017664 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)', null, null, null, 1); } /** * @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.3.1/lib/Horde/Db/Value/Binary.php0000664000175000017500000000174612653711335016452 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 * @since Horde_Db 2.1.0 */ public $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.3.1/lib/Horde/Db/Value/Text.php0000664000175000017500000000170712653711335016147 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ /** * Encapsulation object for long text values to be used in SQL statements to * ensure proper quoting, escaping, retrieval, etc. * * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db */ class Horde_Db_Value_Text implements Horde_Db_Value { /** * Text value to be quoted * * @var string * @since Horde_Db 2.1.0 */ public $value; /** * Constructor * * @param string $textValue */ public function __construct($textValue) { $this->value = $textValue; } /** * @param Horde_Db_Adapter $db */ public function quote(Horde_Db_Adapter $db) { return $db->quoteString($this->value); } } Horde_Db-2.3.1/lib/Horde/Db/Adapter.php0000664000175000017500000002600212653711335015522 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 Horde_Db_Adapter_Base_Result * @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. * * @deprecated Deprecated for external usage. Use select() instead. * * @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 mixed * @throws Horde_Db_Exception */ public function execute($sql, $arg1 = null, $arg2 = null); /** * 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); /** * Inserts a row including BLOBs into a table. * * @since Horde_Db 2.1.0 * * @param string $table The table name. * @param array $fields A hash of column names and values. BLOB columns * must be provided as Horde_Db_Value_Binary * objects. * @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. * * @return integer Last inserted ID. * @throws Horde_Db_Exception */ public function insertBlob($table, $fields, $pk = null, $idValue = 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); /** * Updates rows including BLOBs into a table. * * @since Horde_Db 2.2.0 * * @param string $table The table name. * @param array $fields A hash of column names and values. BLOB columns * must be provided as Horde_Db_Value_Binary objects. * @param string $where A WHERE clause. * * @throws Horde_Db_Exception */ public function updateBlob($table, $fields, $where = ''); /** * 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.3.1/lib/Horde/Db/Exception.php0000664000175000017500000000130112653711335016073 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.3.1/lib/Horde/Db/SearchParser.php0000664000175000017500000001331312653711335016525 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 */ public static 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 = '!' . Horde_String::upper($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); } protected static 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)"; } protected static 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)"; } protected static 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); } protected static 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 = Horde_String::lower(substr(array_shift($tokens), 1)); $val = addslashes(ereg_replace("([\\%])", "\\\\1", $val)); return "(LOWER($column) LIKE '%$val%')"; } } Horde_Db-2.3.1/lib/Horde/Db/StatementParser.php0000664000175000017500000000463512653711335017273 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.3.1/lib/Horde/Db/Value.php0000664000175000017500000000103612653711335015216 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.3.1/lib/Horde/Db.php0000664000175000017500000000177012653711335014147 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.3.1/test/Horde/Db/Adapter/Mysql/ColumnDefinition.php0000664000175000017500000000756312653711335022321 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.3.1/test/Horde/Db/Adapter/Mysql/ColumnTest.php0000664000175000017500000000502312653711335021135 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 Horde_Db_Adapter_ColumnBase { protected $_class = 'Horde_Db_Adapter_Mysql_Column'; /*########################################################################## # Types ##########################################################################*/ public function testTypeInteger() { parent::testTypeInteger(); $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)'); $this->assertFalse($col->isUnsigned()); } public function testTypeIntegerUnsigned() { $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(10) UNSIGNED'); $this->assertTrue($col->isUnsigned()); } /*########################################################################## # Type Cast Values ##########################################################################*/ 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()); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/Mysql/TestTableDefinition.php0000664000175000017500000000142612653711335022743 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_TestTableDefinition extends Horde_Db_Adapter_TestTableDefinition { } Horde_Db-2.3.1/test/Horde/Db/Adapter/Oracle/ColumnDefinition.php0000664000175000017500000000730712653711335022415 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_Oracle_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 varchar2(255)', $col->toSql()); } public function testToSqlLimit() { $col = new Horde_Db_Adapter_Base_ColumnDefinition( $this->conn, 'col_name', 'string', 40 ); $this->assertEquals('col_name varchar2(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 varchar2(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 number(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 number(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 varchar2(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 varchar2(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 varchar2(255) NULL', $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 varchar2(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 varchar2(255) DEFAULT \'test\'', $col->toSql()); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/Oracle/ColumnTest.php0000664000175000017500000000514312653711335021240 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Oracle_ColumnTest extends Horde_Db_Adapter_ColumnBase { protected $_class = 'Horde_Db_Adapter_Oracle_Column'; /*########################################################################## # Types ##########################################################################*/ public function testTypeDecimal() { $col = new $this->_class('age', 'NULL', 'decimal', true, 11, 11, 1); $this->assertEquals('decimal', $col->getType()); } /*########################################################################## # Extract Limit ##########################################################################*/ public function testExtractLimitInt() { $col = new $this->_class('test', 'NULL', 'int', true, 11); $this->assertEquals(11, $col->getLimit()); } public function testExtractLimitVarchar() { $col = new $this->_class('test', 'NULL', 'varchar', true, 255); $this->assertEquals(255, $col->getLimit()); } public function testExtractLimitDecimal() { $col = new $this->_class('test', 'NULL', 'decimal', true, 11, 11, 1); $this->assertEquals('11', $col->getLimit()); } /*########################################################################## # Extract Precision/Scale ##########################################################################*/ public function testExtractPrecisionScale() { $col = new $this->_class('test', 'NULL', 'decimal', true, 12, 12, 1); $this->assertEquals('12', $col->precision()); $this->assertEquals('1', $col->scale()); } /*########################################################################## # Type Cast Values ##########################################################################*/ public function testTypeCastBooleanFalse() { $col = new $this->_class('is_active', '0', 'number', false, null, 1); $this->assertSame(false, $col->getDefault()); } public function testTypeCastBooleanTrue() { $col = new $this->_class('is_active', '1', 'number', false, null, 1); $this->assertSame(true, $col->getDefault()); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/Oracle/TestTableDefinition.php0000664000175000017500000000101212653711335023032 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Oracle_TestTableDefinition extends Horde_Db_Adapter_TestTableDefinition { } Horde_Db-2.3.1/test/Horde/Db/Adapter/Pdo/MysqlTest.php0000664000175000017500000000452512653711335020430 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_Db_Adapter_MysqlBase { protected static function _available() { return extension_loaded('pdo') && in_array('mysql', PDO::getAvailableDrivers()); } protected static function _getConnection($overrides = array()) { $config = Horde_Test_Case::getConfig('DB_ADAPTER_PDO_MYSQL_TEST_CONFIG', __DIR__ . '/..', 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); } /*########################################################################## # Accessor ##########################################################################*/ public function testAdapterName() { $this->assertEquals('PDO_MySQL', $this->_conn->adapterName()); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/Pdo/PgsqlTest.php0000664000175000017500000003551412653711335020413 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_PgsqlTest extends Horde_Db_Adapter_TestBase { public static function setUpBeforeClass() { self::$_reason = 'The pgsql adapter is not available'; if (extension_loaded('pdo') && in_array('pgsql', PDO::getAvailableDrivers())) { self::$_skip = false; list($conn,) = static::_getConnection(); if ($conn) { $conn->disconnect(); } } self::$_columnTest = new Horde_Db_Adapter_Postgresql_ColumnDefinition(); self::$_tableTest = new Horde_Db_Adapter_Postgresql_TestTableDefinition(); } protected static function _getConnection($overrides = array()) { $config = Horde_Test_Case::getConfig('DB_ADAPTER_PDO_PGSQL_TEST_CONFIG', __DIR__ . '/..', 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; } $config = array_merge($config, $overrides); $conn = new Horde_Db_Adapter_Pdo_Pgsql($config); $cache = new Horde_Cache(new Horde_Cache_Storage_Mock()); $conn->setCache($cache); return array($conn, $cache); } /*########################################################################## # 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()); } public function testSupportsInterval() { $this->assertTrue($this->_conn->supportsInterval()); } /*########################################################################## # 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)); } /*########################################################################## # 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 testColumns() { $col = parent::testColumns(); $this->assertEquals(null, $col->getLimit()); $this->assertEquals('integer', $col->getSqlType()); } public function testCreateTableWithSeparatePk() { $pkColumn = parent::testCreateTableWithSeparatePk(); $this->assertEquals('"foo" serial primary key', $pkColumn->toSql()); } 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("foo")); $this->_conn->changeColumn('text_to_binary', 'data', 'binary'); $afterChange = $this->_getColumn('text_to_binary', 'data'); $this->assertEquals('bytea', $afterChange->getSqlType()); $this->assertEquals( "foo", 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 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 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) ELSE 0 END", $this->_conn->buildClause('bitmap', '&', 2)); $this->assertEquals( array("CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) & ?) ELSE 0 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) ELSE 0 END", $this->_conn->buildClause('bitmap', '|', 2)); $this->assertEquals( array("CASE WHEN CAST(bitmap AS VARCHAR) ~ '^-?[0-9]+$' THEN (CAST(bitmap AS INTEGER) | ?) ELSE 0 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)); } /*########################################################################## # Protected ##########################################################################*/ /** * Create table to perform tests on */ protected function _createTestTable($name, $options = array()) { parent::_createTestTable($name, $options = array()); try { // make sure table was created $sql = "INSERT INTO $name VALUES (1, 'mlb', 'f')"; $this->_conn->insert($sql); } catch (Exception $e) { } } } Horde_Db-2.3.1/test/Horde/Db/Adapter/Pdo/SqliteTest.php0000664000175000017500000003641612653711335020570 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_SqliteTest extends Horde_Db_Adapter_TestBase { public static function setUpBeforeClass() { self::$_reason = 'The sqlite adapter is not available'; if (extension_loaded('pdo') && in_array('sqlite', PDO::getAvailableDrivers())) { self::$_skip = false; list($conn,) = static::_getConnection(); $conn->disconnect(); } self::$_columnTest = new Horde_Db_Adapter_Sqlite_ColumnDefinition(); self::$_tableTest = new Horde_Db_Adapter_Sqlite_TestTableDefinition(); } protected 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); //$conn->setLogger(new Horde_Log_Logger(new Horde_Log_Handler_Cli())); return array($conn, $cache); } /*########################################################################## # 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()); } } public function testSupportsInterval() { $this->assertFalse($this->_conn->supportsInterval()); } /*########################################################################## # 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)); } /*########################################################################## # 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 testColumns() { $col = parent::testColumns(); $this->assertEquals(null, $col->getLimit()); $this->assertEquals('INTEGER', $col->getSqlType()); } public function testCreateTableWithSeparatePk() { $pkColumn = parent::testCreateTableWithSeparatePk(); $this->assertEquals('"foo" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL', $pkColumn->toSql()); } 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("foo")); $this->_conn->changeColumn('text_to_binary', 'data', 'binary'); $afterChange = $this->_getColumn('text_to_binary', 'data'); $this->assertEquals('blob', $afterChange->getSqlType()); $this->assertEquals( "foo", $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 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 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,) = static::_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); } /*########################################################################## # Protected ##########################################################################*/ /** * Create table to perform tests on */ protected function _createTestTable($name, $options = array()) { parent::_createTestTable($name, $options = array()); try { // make sure table was created $sql = "INSERT INTO $name VALUES (1, 'mlb', 0)"; $this->_conn->insert($sql); } catch (Exception $e) { } } } Horde_Db-2.3.1/test/Horde/Db/Adapter/Postgresql/ColumnDefinition.php0000664000175000017500000000666212653711335023356 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_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" 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.3.1/test/Horde/Db/Adapter/Postgresql/ColumnTest.php0000664000175000017500000001211512653711335022173 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_Db_Adapter_ColumnBase { protected $_class = 'Horde_Db_Adapter_Postgresql_Column'; /*########################################################################## # 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 testTypeString() { $col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)'); $this->assertEquals('string', $col->getType()); } /*########################################################################## # Extract Limit ##########################################################################*/ public function testExtractLimitVarchar() { $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)'); $this->assertEquals(255, $col->getLimit()); } /*########################################################################## # Type Cast Values ##########################################################################*/ 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 testDefaultString() { $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'character varying(255)'); $this->assertEquals('', $col->getDefault()); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/Postgresql/TestTableDefinition.php0000664000175000017500000000143312653711335023777 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_TestTableDefinition extends Horde_Db_Adapter_TestTableDefinition { } Horde_Db-2.3.1/test/Horde/Db/Adapter/Sqlite/ColumnDefinition.php0000664000175000017500000000716612653711335022454 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_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 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.3.1/test/Horde/Db/Adapter/Sqlite/ColumnTest.php0000664000175000017500000000261212653711335021272 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_Db_Adapter_ColumnBase { protected $_class = 'Horde_Db_Adapter_Sqlite_Column'; /*########################################################################## # Type Cast Values ##########################################################################*/ 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()); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/Sqlite/TestTableDefinition.php0000664000175000017500000000142712653711335023100 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_TestTableDefinition extends Horde_Db_Adapter_TestTableDefinition { } Horde_Db-2.3.1/test/Horde/Db/Adapter/ColumnBase.php0000664000175000017500000001755112653711335017774 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 */ abstract class Horde_Db_Adapter_ColumnBase extends Horde_Test_Case { protected $_class; /*########################################################################## # Construction ##########################################################################*/ public function testDefaultNull() { $col = new $this->_class('name', 'NULL', 'varchar(255)'); $this->assertEquals(true, $col->isNull()); } public function testNotNull() { $col = new $this->_class('name', 'NULL', 'varchar(255)', false); $this->assertEquals(false, $col->isNull()); } public function testName() { $col = new $this->_class('name', 'NULL', 'varchar(255)'); $this->assertEquals('name', $col->getName()); } public function testSqlType() { $col = new $this->_class('name', 'NULL', 'varchar(255)'); $this->assertEquals('varchar(255)', $col->getSqlType()); } public function testIsText() { $col = new $this->_class('test', 'NULL', 'varchar(255)'); $this->assertTrue($col->isText()); $col = new $this->_class('test', 'NULL', 'text'); $this->assertTrue($col->isText()); $col = new $this->_class('test', 'NULL', 'int(11)'); $this->assertFalse($col->isText()); $col = new $this->_class('test', 'NULL', 'float(11,1)'); $this->assertFalse($col->isText()); } public function testIsNumber() { $col = new $this->_class('test', 'NULL', 'varchar(255)'); $this->assertFalse($col->isNumber()); $col = new $this->_class('test', 'NULL', 'text'); $this->assertFalse($col->isNumber()); $col = new $this->_class('test', 'NULL', 'int(11)'); $this->assertTrue($col->isNumber()); $col = new $this->_class('test', 'NULL', 'float(11,1)'); $this->assertTrue($col->isNumber()); } /*########################################################################## # Types ##########################################################################*/ public function testTypeInteger() { $col = new $this->_class('age', 'NULL', 'int(11)'); $this->assertEquals('integer', $col->getType()); } public function testTypeFloat() { $col = new $this->_class('age', 'NULL', 'float(11,1)'); $this->assertEquals('float', $col->getType()); } public function testTypeDecimalPrecisionNone() { $col = new $this->_class('age', 'NULL', 'decimal(11,0)'); $this->assertEquals('integer', $col->getType()); } public function testTypeDecimal() { $col = new $this->_class('age', 'NULL', 'decimal(11,1)'); $this->assertEquals('decimal', $col->getType()); } public function testTypeDatetime() { $col = new $this->_class('age', 'NULL', 'datetime'); $this->assertEquals('datetime', $col->getType()); } public function testTypeTimestamp() { $col = new $this->_class('age', 'CURRENT_TIMESTAMP', 'timestamp'); $this->assertEquals('timestamp', $col->getType()); } public function testTypeTime() { $col = new $this->_class('age', 'NULL', 'time'); $this->assertEquals('time', $col->getType()); } public function testTypeDate() { $col = new $this->_class('age', 'NULL', 'date'); $this->assertEquals('date', $col->getType()); } public function testTypeText() { $col = new $this->_class('age', 'NULL', 'text'); $this->assertEquals('text', $col->getType()); } public function testTypeBinary() { $col = new $this->_class('age', 'NULL', 'blob(255)'); $this->assertEquals('binary', $col->getType()); } public function testTypeString() { $col = new $this->_class('name', 'NULL', 'varchar(255)'); $this->assertEquals('string', $col->getType()); } /*########################################################################## # Extract Limit ##########################################################################*/ public function testExtractLimitInt() { $col = new $this->_class('test', 'NULL', 'int(11)'); $this->assertEquals(11, $col->getLimit()); } public function testExtractLimitVarchar() { $col = new $this->_class('test', 'NULL', 'varchar(255)'); $this->assertEquals(255, $col->getLimit()); } public function testExtractLimitDecimal() { $col = new $this->_class('test', 'NULL', 'decimal(11,1)'); $this->assertEquals('11', $col->getLimit()); } public function testExtractLimitText() { $col = new $this->_class('test', 'NULL', 'text'); $this->assertEquals(null, $col->getLimit()); } public function testExtractLimitNone() { $col = new $this->_class('test', 'NULL'); $this->assertEquals(null, $col->getLimit()); } /*########################################################################## # Extract Precision/Scale ##########################################################################*/ public function testExtractPrecisionScale() { $col = new $this->_class('test', 'NULL', 'decimal(12,1)'); $this->assertEquals('12', $col->precision()); $this->assertEquals('1', $col->scale()); } /*########################################################################## # Type Cast Values ##########################################################################*/ public function testTypeCastInteger() { $col = new $this->_class('name', '1', 'int(11)', false); $this->assertEquals(1, $col->getDefault()); } public function testTypeCastFloat() { $col = new $this->_class('version', '1.0', 'float(11,1)', false); $this->assertEquals(1.0, $col->getDefault()); } public function testTypeCastString() { $col = new $this->_class('name', 'n/a', 'varchar(255)', false); $this->assertEquals('n/a', $col->getDefault()); } abstract public function testTypeCastBooleanFalse(); abstract public function testTypeCastBooleanTrue(); /*########################################################################## # Defaults ##########################################################################*/ public function testDefaultDatetime() { $col = new $this->_class('name', '', 'datetime'); $this->assertEquals(null, $col->getDefault()); } public function testDefaultInteger() { $col = new $this->_class('name', '', 'int(11)'); $this->assertEquals(null, $col->getDefault()); } public function testDefaultString() { $col = new $this->_class('name', '', 'varchar(255)'); $this->assertEquals('', $col->getDefault()); } public function testDefaultText() { $col = new $this->_class('name', '', 'text'); $this->assertEquals('', $col->getDefault()); } public function testDefaultBinary() { $col = new $this->_class('name', '', 'blob(255)'); $this->assertEquals('', $col->getDefault()); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/conf.php.dist0000664000175000017500000000236712653711335017632 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 */ abstract class Horde_Db_Adapter_MysqlBase extends Horde_Db_Adapter_TestBase { protected static function _available() { throw new LogicException('_available() must be implemented in a sub-class.'); } public static function setUpBeforeClass() { self::$_reason = 'The MySQL adapter is not available'; if (static::_available()) { self::$_skip = false; list($conn,) = static::_getConnection(); if (self::$_skip) { return; } $conn->disconnect(); } self::$_columnTest = new Horde_Db_Adapter_Mysql_ColumnDefinition(); self::$_tableTest = new Horde_Db_Adapter_Mysql_TestTableDefinition(); } /*########################################################################## # Accessor ##########################################################################*/ public function testSupportsMigrations() { $this->assertTrue($this->_conn->supportsMigrations()); } public function testSupportsCountDistinct() { $this->assertTrue($this->_conn->supportsCountDistinct()); } public function testSupportsInterval() { $this->assertTrue($this->_conn->supportsInterval()); } public function testGetCharset() { $this->assertEquals('utf8', Horde_String::lower($this->_conn->getCharset())); } /*########################################################################## # Database Statements ##########################################################################*/ public function testTransactionCommit() { parent::testTransactionCommit(); // 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() { parent::testTransactionRollback(); // 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, null, null, 'id', 7); // 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)); } /*########################################################################## # 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'); try { $this->_conn->insert('INSERT INTO users (intelligence_quotient) VALUES (300)'); $jonnyg = (object)$this->_conn->selectOne('SELECT * FROM users'); $this->assertEquals('127', $jonnyg->intelligence_quotient); } catch (Horde_Db_Exception $e) { if (strpos($e->getMessage(), "Out of range value for column 'intelligence_quotient' at row 1") === false) { throw $e; } } } public function testTableAliasLength() { $len = $this->_conn->tableAliasLength(); $this->assertEquals(255, $len); } public function testColumns() { $col = parent::testColumns(); $this->assertEquals(10, $col->getLimit()); $this->assertEquals(true, $col->isUnsigned()); $this->assertEquals('int(10) unsigned', $col->getSqlType()); } public function testCreateTableWithSeparatePk() { $pkColumn = parent::testCreateTableWithSeparatePk(); $this->assertEquals('`foo` int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY', $pkColumn->toSql()); } 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")); $this->_conn->changeColumn('text_to_binary', 'data', 'binary'); $afterChange = $this->_getColumn('text_to_binary', 'data'); $this->assertEquals('longblob', $afterChange->getSqlType()); $this->assertEquals( "foo", $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)'); try { $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); } catch (Horde_Db_Exception $e) { if (strpos($e->getMessage(), "Out of range value for column 'foo' at row 1") === false) { throw $e; } } } 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 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 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,) = static::_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); } /*########################################################################## # Protected ##########################################################################*/ /** * Create table to perform tests on */ protected function _createTestTable($name, $options = array()) { parent::_createTestTable($name, $options = array()); try { // make sure table was created $sql = "INSERT INTO $name VALUES (1, 'mlb', 0)"; $this->_conn->insert($sql); } catch (Exception $e) { } } public function testColumnToSqlUnsigned() { self::$_columnTest->testToSqlUnsigned(); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/MysqliTest.php0000664000175000017500000000443612653711335020060 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_Db_Adapter_MysqlBase { protected static function _available() { return extension_loaded('mysqli'); } protected 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']) && is_array($config['db']['adapter']['mysqli']['test'])) { $config = $config['db']['adapter']['mysqli']['test']; } else { 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); } /*########################################################################## # Accessor ##########################################################################*/ public function testAdapterName() { $this->assertEquals('MySQLi', $this->_conn->adapterName()); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/MysqlTest.php0000664000175000017500000000442512653711335017705 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_Db_Adapter_MysqlBase { protected static function _available() { return extension_loaded('mysql'); } protected 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']) && is_array($config['db']['adapter']['mysql']['test'])) { $config = $config['db']['adapter']['mysql']['test']; } else { 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); } /*########################################################################## # Accessor ##########################################################################*/ public function testAdapterName() { $this->assertEquals('MySQL', $this->_conn->adapterName()); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/Oci8Test.php0000664000175000017500000003452212653711335017403 0ustar janjan * @license http://www.horde.org/licenses/bsd * @category Horde * @package Db * @subpackage UnitTests */ /** * @author Jan Schneider * @license http://www.horde.org/licenses/bsd * @group horde_db * @category Horde * @package Db * @subpackage UnitTests */ class Horde_Db_Adapter_Oci8Test extends Horde_Db_Adapter_TestBase { public static function setUpBeforeClass() { self::$_reason = 'The OCI8 adapter is not available'; if (extension_loaded('oci8')) { self::$_skip = false; list($conn,) = static::_getConnection(); if (self::$_skip) { return; } $conn->disconnect(); } self::$_columnTest = new Horde_Db_Adapter_Oracle_ColumnDefinition(); self::$_tableTest = new Horde_Db_Adapter_Oracle_TestTableDefinition(); } protected static function _getConnection($overrides = array()) { $config = Horde_Test_Case::getConfig('DB_ADAPTER_OCI8_TEST_CONFIG', null, array('host' => 'localhost', 'username' => '', 'password' => '', 'dbname' => 'test')); if (isset($config['db']['adapter']['oci8']['test']) && is_array($config['db']['adapter']['oci8']['test'])) { $config = $config['db']['adapter']['oci8']['test']; } else { self::$_skip = true; self::$_reason = 'No configuration for oci8 test'; return; } $config = array_merge($config, $overrides); $conn = new Horde_Db_Adapter_Oci8($config); $cache = new Horde_Cache(new Horde_Cache_Storage_Mock()); $conn->setCache($cache); //$conn->setLogger(new Horde_Log_Logger(new Horde_Log_Handler_Cli())); $conn->reconnect(); return array($conn, $cache); } /*########################################################################## # Accessor ##########################################################################*/ public function testAdapterName() { $this->assertEquals('Oracle', $this->_conn->adapterName()); } public function testSupportsMigrations() { $this->assertTrue($this->_conn->supportsMigrations()); } public function testSupportsCountDistinct() { $this->assertTrue($this->_conn->supportsCountDistinct()); } public function testSupportsInterval() { $this->assertTrue($this->_conn->supportsInterval()); } /*########################################################################## # 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_Oracle_Column('age', 'NULL', 'int', true, 11); $this->assertEquals('1', $this->_conn->quote(true, $col)); } /*########################################################################## # Schema Statements ##########################################################################*/ public function testNativeDatabaseTypes() { $types = $this->_conn->nativeDatabaseTypes(); $this->assertEquals(array('name' => 'number', 'limit' => null), $types['integer']); } public function testTableAliasLength() { $len = $this->_conn->tableAliasLength(); $this->assertEquals(30, $len); } public function testChangeColumnType() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('number', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string'); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar2', $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')); $this->_conn->insert('INSERT INTO text_to_binary (data) VALUES (?)', array(null)); $this->_conn->changeColumn('text_to_binary', 'data', 'binary'); $afterChange = $this->_getColumn('text_to_binary', 'data'); $this->assertEquals('blob', $afterChange->getSqlType()); $values = $this->_conn->selectValues('SELECT data FROM text_to_binary'); $this->assertInstanceOf('OCI-Lob', $values[0]); $this->assertEquals('foo', $values[0]->read($values[0]->size())); $this->assertEquals(null, $values[1]); } public function testChangeColumnLimit() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('number', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'string', array('limit' => '40')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('varchar2', $afterChange->getSqlType()); $this->assertEquals(40, $afterChange->getLimit()); } public function testChangeColumnPrecisionScale() { $this->_createTestTable('sports'); $beforeChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('number', $beforeChange->getSqlType()); $this->_conn->changeColumn('sports', 'is_college', 'decimal', array('precision' => '5', 'scale' => '2')); $afterChange = $this->_getColumn('sports', 'is_college'); $this->assertEquals('number', $afterChange->getSqlType()); $this->assertEquals(5, $afterChange->precision()); $this->assertEquals(2, $afterChange->scale()); } 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('number', $beforeChange->getSqlType()); $this->_conn->renameColumn('sports', 'is_college', 'is_renamed'); $afterChange = $this->_getColumn('sports', 'is_renamed'); $this->assertEquals('number', $afterChange->getSqlType()); } public function testIndexNameByMultiColumn() { $name = $this->_conn->indexName('sports', array('column' => array('name', 'is_college'))); $this->assertEquals('ind_sports_5ca2d9c7', $name); } public function testTypeToSqlTypePrimaryKey() { $result = $this->_conn->typeToSql('autoincrementKey'); $this->assertEquals('number NOT NULL PRIMARY KEY', $result); } public function testTypeToSqlTypeString() { $result = $this->_conn->typeToSql('string'); $this->assertEquals('varchar2(255)', $result); } public function testTypeToSqlTypeText() { $result = $this->_conn->typeToSql('text'); $this->assertEquals('clob', $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('date', $result); } public function testTypeToSqlTypeTimestamp() { $result = $this->_conn->typeToSql('timestamp'); $this->assertEquals('date', $result); } public function testTypeToSqlInt() { $result = $this->_conn->typeToSql('integer'); $this->assertEquals('number', $result); } public function testTypeToSqlIntLimit() { $result = $this->_conn->typeToSql('integer', '1'); $this->assertEquals('number(1)', $result); } public function testTypeToSqlDecimalPrecision() { $result = $this->_conn->typeToSql('decimal', null, '5'); $this->assertEquals('number(5)', $result); } public function testTypeToSqlDecimalScale() { $result = $this->_conn->typeToSql('decimal', null, '5', '2'); $this->assertEquals('number(5, 2)', $result); } public function testTypeToSqlBoolean() { $result = $this->_conn->typeToSql('boolean'); $this->assertEquals('number(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 NULL', $result); } public function testAddColumnOptionsNotNull() { $options = array('null' => false); $result = $this->_conn->addColumnOptions('test', $options); $this->assertEquals('test NOT NULL', $result); } /** * @depends testQuoteBinary */ public function testBug14163() { $blob = new Horde_Db_Value_Binary('foo'); $this->_conn->insertBlob('binary_testings', array('data' => $blob)); $this->_conn->updateBlob('binary_testings', array('data' => '')); $this->_conn->insertBlob('binary_testings', array('data' => '')); } 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( 'BITAND(bitmap, 2)', $this->_conn->buildClause('bitmap', '&', 2)); $this->assertEquals( array('BITAND(bitmap, ?)', array(2)), $this->_conn->buildClause('bitmap', '&', 2, true)); $this->assertEquals( 'bitmap + 2 - BITAND(bitmap, 2)', $this->_conn->buildClause('bitmap', '|', 2)); $this->assertEquals( array('bitmap + ? - BITAND(bitmap, ?)', array(2, 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)); } } Horde_Db-2.3.1/test/Horde/Db/Adapter/TestBase.php0000664000175000017500000010635712653711335017461 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 */ abstract class Horde_Db_Adapter_TestBase extends Horde_Test_Case { protected static $_columnTest; protected static $_tableTest; protected static $_skip = true; protected static $_reason; protected $_conn; protected static function _getConnection($overrides = array()) { throw new LogicException('_getConnection() must be implemented in a sub-class.'); } protected function setUp() { if (self::$_skip || !($res = static::_getConnection())) { $this->markTestSkipped(self::$_reason); } list($this->_conn, $this->_cache) = $res; self::$_columnTest->conn = $this->_conn; self::$_tableTest->conn = $this->_conn; // clear out detritus from any previous test runs. $this->_dropTestTables(); } protected function tearDown() { if ($this->_conn) { // 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 ##########################################################################*/ abstract public function testAdapterName(); abstract public function testSupportsMigrations(); abstract public function testSupportsCountDistinct(); abstract public function testSupportsInterval(); /*########################################################################## # Database Statements ##########################################################################*/ public function testSelect() { $this->_createTable(); $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() { $this->_createTable(); $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() { $this->_createTable(); $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() { $this->_createTable(); $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() { $this->_createTable(); $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() { $this->_createTable(); $sql = "SELECT * FROM unit_tests WHERE id='1'"; $result = $this->_conn->selectValue($sql); $this->assertEquals(1, $result); } public function testSelectValues() { $this->_createTable(); $sql = "SELECT * FROM unit_tests"; $result = $this->_conn->selectValues($sql); $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result); } public function testInsert() { $this->_createTable(); $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 testInsertBlob() { $this->_createTable(); $result = $this->_conn->insertBlob( 'unit_tests', array( 'id' => 7, 'integer_value' => 999, 'blob_value' => new Horde_Db_Value_Binary(str_repeat("\0", 5000)) ), null, 7 ); $this->assertEquals(7, $result); $result = $this->_conn->insertBlob( 'unit_tests', array( 'id' => 8, 'integer_value' => 1000, 'text_value' => new Horde_Db_Value_Text(str_repeat('X', 5000)) ), null, 8 ); $this->assertEquals(8, $result); } public function testUpdate() { $this->_createTable(); $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)"; $result = $this->_conn->update($sql); $this->assertEquals(1, $result); } public function testUpdateBlob() { $this->_createTable(); $result = $this->_conn->updateBlob( 'unit_tests', array( 'blob_value' => new Horde_Db_Value_Binary(str_repeat("\0", 5000)) ), 'id = 1' ); $this->assertEquals(1, $result); $result = $this->_conn->updateBlob( 'unit_tests', array( 'text_value' => new Horde_Db_Value_Text(str_repeat('X', 5000)) ), 'id = 1' ); $this->assertEquals(1, $result); } public function testDelete() { $this->_createTable(); $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->_createTable(); $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->_createTable(); $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 ##########################################################################*/ abstract public function testQuoteNull(); abstract public function testQuoteTrue(); abstract public function testQuoteFalse(); abstract public function testQuoteInteger(); abstract public function testQuoteFloat(); abstract public function testQuoteString(); abstract public function testQuoteDirtyString(); abstract public function testQuoteColumnName(); 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 ##########################################################################*/ abstract public function testNativeDatabaseTypes(); abstract public function testTableAliasLength(); public function testTableAliasFor() { $alias = $this->_conn->tableAliasFor('my_table_name'); $this->assertEquals('my_table_name', $alias); } public function testTables() { $this->_createTable(); $tables = $this->_conn->tables(); $this->assertTrue(count($tables) > 0); $this->assertContains('unit_tests', $tables); } public function testPrimaryKey() { $this->_createTable(); $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() { $this->_createTable(); $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() { $this->_createTable(); $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('', $col->getDefault()); $this->assertEquals(false, $col->isText()); $this->assertEquals(true, $col->isNumber()); return $col; } 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->_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)); // 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)'); return $pkColumn; } abstract public function testChangeColumnType(); abstract public function testChangeColumnLimit(); abstract public function testChangeColumnPrecisionScale(); public function testChangeColumnNull() { $this->_createTestTable('sports'); $column = $this->_getColumn('sports', 'name'); $this->assertTrue($column->isNull()); $this->_conn->changeColumn('sports', 'name', 'string', array('null' => false)); $column = $this->_getColumn('sports', 'name'); $this->assertFalse($column->isNull()); $this->_conn->changeColumn('sports', 'name', 'string', array('null' => true)); $column = $this->_getColumn('sports', 'name'); $this->assertTrue($column->isNull()); } abstract public function testRenameColumn(); public function testRenameColumnWithSqlReservedWord() { $this->_createTestUsersTable(); $this->_conn->renameColumn('users', 'first_name', 'other_name'); $this->assertTrue(in_array('other_name', $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')) ); $index = $this->_conn->addIndex( 'users', array('last_name', 'first_name') ); $this->_conn->removeIndex('users', array('name' => $index)); $this->_conn->addIndex('users', array('last_name', 'first_name')); $this->_conn->removeIndex('users', 'last_name_and_first_name'); // quoting $index = $this->_conn->addIndex( 'users', array('key'), array('name' => 'key_idx', 'unique' => true) ); $this->_conn->removeIndex( 'users', array('name' => $index, 'unique' => true) ); $index = $this->_conn->addIndex( 'users', array('last_name', 'first_name', 'administrator'), array('name' => 'named_admin') ); $this->_conn->removeIndex('users', array('name' => $index)); } 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); } abstract public function testTypeToSqlTypePrimaryKey(); abstract public function testTypeToSqlTypeString(); abstract public function testTypeToSqlTypeText(); abstract public function testTypeToSqlTypeBinary(); abstract public function testTypeToSqlTypeFloat(); abstract public function testTypeToSqlTypeDatetime(); abstract public function testTypeToSqlTypeTimestamp(); abstract public function testTypeToSqlInt(); abstract public function testTypeToSqlIntLimit(); abstract public function testTypeToSqlDecimalPrecision(); abstract public function testTypeToSqlDecimalScale(); abstract public function testTypeToSqlBoolean(); abstract public function testAddColumnOptions(); abstract public function testAddColumnOptionsDefault(); abstract public function testAddColumnOptionsNull(); abstract public function testAddColumnOptionsNotNull(); 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')"); $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); } abstract public function testModifyDate(); abstract public function testBuildClause(); public function testInsertAndReadInUtf8() { list($conn,) = static::_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); } /*########################################################################## # Autoincrement Management ##########################################################################*/ public function testAutoIncrementWithTypeInColumn() { $table = $this->_conn->createTable('autoinc', array('autoincrementKey' => false)); $table->column('foo', 'autoincrementKey'); $table->column('bar', 'integer'); $table->end(); try { $this->assertEquals(1, $this->_conn->insert('INSERT INTO autoinc (bar) VALUES(5)')); } catch (Exception $e) { var_dump($this->_conn->getLastQuery()); throw $e; } $this->assertEquals(2, $this->_conn->insert('INSERT INTO autoinc (bar) VALUES(6)')); $this->assertEquals(2, $this->_conn->selectValue('SELECT foo FROM autoinc WHERE bar = 6')); } /** * @expectedException LogicException * @expectedExceptionMessage foo has already been added as a primary key */ public function testAutoIncrementWithTypeInTableAndColumnDefined() { $table = $this->_conn->createTable('autoincrement', array('autoincrementKey' => 'foo')); $table->column('foo', 'integer'); $table->column('bar', 'integer'); $table->end(); } public function testAutoIncrementWithTypeInTable() { $table = $this->_conn->createTable('autoinc', array('autoincrementKey' => 'foo')); $table->column('bar', 'integer'); $table->end(); $this->assertEquals(1, $this->_conn->insert('INSERT INTO autoinc (bar) VALUES(5)')); $this->assertEquals(2, $this->_conn->insert('INSERT INTO autoinc (bar) VALUES(6)')); $this->assertEquals(2, $this->_conn->selectValue('SELECT foo FROM autoinc WHERE bar = 6')); } public function testAutoIncrementWithAddColumn() { $table = $this->_conn->createTable('autoinc', array('autoincrementKey' => false)); $table->column('bar', 'integer'); $table->end(); $this->_conn->addColumn('autoinc', 'foo', 'autoincrementKey'); $this->assertEquals(1, $this->_conn->insert('INSERT INTO autoinc (bar) VALUES(5)')); $this->assertEquals(2, $this->_conn->insert('INSERT INTO autoinc (bar) VALUES(6)')); $this->assertEquals(2, $this->_conn->selectValue('SELECT foo FROM autoinc WHERE bar = 6')); } public function testAutoIncrementWithChangeColumn() { $table = $this->_conn->createTable('autoinc', array('autoincrementKey' => false)); $table->column('foo', 'integer'); $table->column('bar', 'integer'); $table->end(); $this->_conn->changeColumn('autoinc', 'foo', 'autoincrementKey'); $this->assertEquals(1, $this->_conn->insert('INSERT INTO autoinc (bar) VALUES(5)')); $this->assertEquals(2, $this->_conn->insert('INSERT INTO autoinc (bar) VALUES(6)')); $this->assertEquals(2, $this->_conn->selectValue('SELECT foo FROM autoinc WHERE bar = 6')); } /*########################################################################## # Table cache ##########################################################################*/ public function testCachedTableIndexes() { // remove any current cache. $this->_conn->cacheWrite('tables/indexes/cache_table', ''); $this->assertEquals('', $this->_conn->cacheRead('tables/indexes/cache_table')); $this->_createTestTable('cache_table'); $idxs = $this->_conn->indexes('cache_table'); $this->assertNotEquals('', $this->_conn->cacheRead('tables/indexes/cache_table')); } public function testCachedTableColumns() { // remove any current cache. $this->_conn->cacheWrite('tables/columns/cache_table', ''); $this->assertEquals('', $this->_conn->cacheRead('tables/columns/cache_table')); $this->_createTestTable('cache_table'); $cols = $this->_conn->columns('cache_table'); $this->assertNotEquals('', $this->_conn->cacheRead('tables/columns/cache_table')); } /*########################################################################## # Protected ##########################################################################*/ protected function _createTable() { $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()); $table->column('float_value', 'float', array('precision' => 2, '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()); $table->column('blob_value', 'binary', array()); $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->insert($stmt); } } protected function _createTestTable($name, $options = array()) { $table = $this->_conn->createTable($name, $options); $table->column('name', 'string'); $table->column('is_college', 'boolean'); $table->end(); } 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( 'autoinc', 'binary_testings', 'cache_table', /* MySQL only? */ 'charset_cp1257', /* MySQL only? */ '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 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.3.1/test/Horde/Db/Adapter/TestTableDefinition.php0000664000175000017500000000257512653711335021644 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_TestTableDefinition { 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.3.1/test/Horde/Db/fixtures/charsets/cp1257.txt0000664000175000017500000000001112653711335021001 0ustar janjanAð þinau Horde_Db-2.3.1/test/Horde/Db/fixtures/charsets/utf8.txt0000664000175000017500000000000412653711335020750 0ustar janjan☃ Horde_Db-2.3.1/test/Horde/Db/fixtures/migrations/1_users_have_last_names1.php0000664000175000017500000000037612653711335025261 0ustar janjanaddColumn('users', 'last_name', 'string'); } public function down() { $this->removeColumn('users', 'last_name'); } }Horde_Db-2.3.1/test/Horde/Db/fixtures/migrations/2_we_need_reminders1.php0000664000175000017500000000052412653711335024361 0ustar janjancreateTable('reminders'); $t->column('content', 'text'); $t->column('remind_at', 'datetime'); $t->end(); } public function down() { $this->dropTable('reminders'); } }Horde_Db-2.3.1/test/Horde/Db/fixtures/migrations/3_innocent_jointable1.php0000664000175000017500000000061412653711335024550 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.3.1/test/Horde/Db/fixtures/migrations_with_decimal/1_give_me_big_numbers.php0000664000175000017500000000127312653711335027323 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.3.1/test/Horde/Db/fixtures/migrations_with_duplicate/1_users_have_last_names2.php0000664000175000017500000000037612653711335030347 0ustar janjanaddColumn('users', 'last_name', 'string'); } public function down() { $this->removeColumn('users', 'last_name'); } }Horde_Db-2.3.1/test/Horde/Db/fixtures/migrations_with_duplicate/2_we_need_reminders2.php0000664000175000017500000000052412653711335027447 0ustar janjancreateTable('reminders'); $t->column('content', 'text'); $t->column('remind_at', 'datetime'); $t->end(); } public function down() { $this->dropTable('reminders'); } }Horde_Db-2.3.1/test/Horde/Db/fixtures/migrations_with_duplicate/3_foo.php0000664000175000017500000000020112653711335024463 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.3.1/test/Horde/Db/fixtures/migrations_with_missing_versions/1_users_have_last_names3.php0000664000175000017500000000037612653711335031777 0ustar janjanaddColumn('users', 'last_name', 'string'); } public function down() { $this->removeColumn('users', 'last_name'); } }Horde_Db-2.3.1/test/Horde/Db/fixtures/migrations_with_missing_versions/3_we_need_reminders.php0000664000175000017500000000052312653711335031014 0ustar janjancreateTable('reminders'); $t->column('content', 'text'); $t->column('remind_at', 'datetime'); $t->end(); } public function down() { $this->dropTable('reminders'); } } Horde_Db-2.3.1/test/Horde/Db/fixtures/migrations_with_missing_versions/4_innocent_jointable3.php0000664000175000017500000000061412653711335031267 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.3.1/test/Horde/Db/fixtures/migrations_with_missing_versions/1000_users_have_middle_names.phpHorde_Db-2.3.1/test/Horde/Db/fixtures/migrations_with_missing_versions/1000_users_have_middle_names.0000664000175000017500000000040312653711335031706 0ustar janjanaddColumn('users', 'middle_name', 'string'); } public function down() { $this->removeColumn('users', 'middle_name'); } }Horde_Db-2.3.1/test/Horde/Db/fixtures/drop_create_table.sql0000664000175000017500000000114012653711335021666 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.3.1/test/Horde/Db/fixtures/unit_tests.sql0000664000175000017500000001431712653711335020443 0ustar janjan-- -- Copyright 2007 Maintainable Software, LLC -- Copyright 2008-2016 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', '736f6d6520626c6f622064617461', '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', '736f6d6520626c6f622064617461', '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', '736f6d6520626c6f622064617461', '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', '736f6d6520626c6f622064617461', '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', '736f6d6520626c6f622064617461', '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', '736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f622064617461736f6d6520626c6f62206461', '0', 'foo@example.com'); Horde_Db-2.3.1/test/Horde/Db/Migration/BaseTest.php0000664000175000017500000001454212653711335020024 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.3.1/test/Horde/Db/Migration/MigratorTest.php0000664000175000017500000002000312653711335020723 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.3.1/test/Horde/Db/AllTests.php0000664000175000017500000000013212653711335016102 0ustar janjanrun(); Horde_Db-2.3.1/test/Horde/Db/bootstrap.php0000664000175000017500000000014312653711335016366 0ustar janjan Horde_Db-2.3.1/test/Horde/Db/StatementParserTest.php0000664000175000017500000000247512653711335020344 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"); } } }