--- /dev/null
+D/doc////
+D/lib////
+D/test////
+/package.xml/1.3/Fri Sep 26 12:36:30 2008//
--- /dev/null
+framework/Db
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+/TODO.txt/1.5/Wed Oct 22 01:41:15 2008//
+D
--- /dev/null
+framework/Db/doc
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+- port Rdo to run on Horde_Db
+
+- document running the test suite
+
+- 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.
+
+- replace backend-specific structuredump with a YAML/JSON dump of schema and
+ data, including a corresponding importer. should look at what it would take to
+ support MDB2_Schema XML instead, and at what Doctrine does, but a simple
+ YAML/JSON structure that represents the PHP objects (TableDefinition,
+ ColumnDefinition, IndexDefinition) 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.
+
+- Handle Horde_Date objects in both input/output from Horde_Db.
+
+- write a make task to generate .sql scripts (for a specific db backend) from
+ the schema/data files. might want to automatically generate these for release
+ tarballs too.
+
+- migrations. either rails-style, or MDB2 diff between one parsed structure and
+ another, or both.
+
+- 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.
+
+- port any updates to mysql adapter
+
+- port any updates to pgsql adapter
+
+- composite primary key support: http://compositekeys.rubyforge.org/
+
+- port disable_referential_integrity?
+
+- port query_cache.rb
+
+- what to do about '0000-00-00' with PostgreSQL?
--- /dev/null
+D/Horde////
--- /dev/null
+framework/Db/lib
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+D/Db////
+/Db.php/1.1/Fri Sep 19 03:59:03 2008//
--- /dev/null
+framework/Db/lib/Horde
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2006-2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php BSD
+ * @category Horde
+ * @package Horde_Db
+ */
+
+/**
+ * Horde_Db namespace - holds constants and global Db functions.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php BSD
+ * @category Horde
+ * @package Horde_Db
+ */
+class Horde_Db
+{
+ /**
+ * Global adapter object.
+ *
+ * @var Horde_Db_Adapter
+ */
+ protected static $_adapter;
+
+ /**
+ * Get the global adapter object.
+ *
+ * @return Horde_Db_Adapter
+ */
+ public static function getAdapter()
+ {
+ return self::$_adapter;
+ }
+
+ /**
+ * Set a global database adapter.
+ *
+ * @param Horde_Db_Adapter $adapter
+ */
+ public static function setAdapter($adapter)
+ {
+ self::$_adapter = $adapter;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2006-2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * Abstract parent class for Db Adapters.
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter
+{
+ /**
+ * Handle Horde-style configuration arrays, PEAR DB/MDB2 arrays or DSNs, or
+ * PDO DSNS.
+ */
+ public static function getInstance($config)
+ {
+ $adapter = str_replace(' ', '_' , ucwords(str_replace('_', ' ', basename($config['adapter']))));
+ $class = 'Horde_Db_Adapter_' . $adapter;
+ if (!class_exists($class)) {
+ throw new Horde_Db_Exception('Adapter class "' . $class . '" not found');
+ }
+
+ return new $class($config);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+abstract class Horde_Db_Adapter_Abstract
+{
+ /**
+ * Config options
+ * @var array
+ */
+ protected $_config = array();
+
+ /**
+ * @var mixed
+ */
+ protected $_connection = null;
+
+ /**
+ * @var boolean
+ */
+ protected $_transactionStarted = false;
+
+ /**
+ * @var int
+ */
+ protected $_rowCount = null;
+
+ /**
+ * @var int
+ */
+ protected $_runtime = null;
+
+ /**
+ * @var boolean
+ */
+ protected $_active = null;
+
+ /**
+ * @var Cache object
+ */
+ protected $_cache = null;
+
+ /**
+ * @var Logger
+ */
+ protected $_logger = null;
+
+ /**
+ * @var Horde_Db_Adapter_Abstract_Schema
+ */
+ protected $_schema = null;
+
+ /**
+ * @var string
+ */
+ protected $_schemaClass = null;
+
+ /**
+ * @var array
+ */
+ protected $_schemaMethods = array();
+
+
+ /*##########################################################################
+ # Construct/Destruct
+ ##########################################################################*/
+
+ /**
+ * @param array $config Configuration options and optional objects (logger,
+ * cache, etc.)
+ */
+ public function __construct($config)
+ {
+ // Create a stub if we don't have a useable cache.
+ if (isset($config['cache'])
+ && is_callable(array($config['cache'], 'get'))
+ && is_callable(array($config['cache'], 'set'))) {
+ $this->_cache = $config['cache'];
+ unset($config['cache']);
+ } else {
+ $this->_cache = new Horde_Support_Stub;
+ }
+
+ // Create a stub if we don't have a useable logger.
+ if (isset($config['logger'])
+ && is_callable(array($config['logger'], 'log'))) {
+ $this->_logger = $config['logger'];
+ unset($config['logger']);
+ } else {
+ $this->_logger = new Horde_Support_Stub;
+ }
+
+ $this->_config = $config;
+ $this->_runtime = 0;
+
+ // Create the database-specific (but not adapter specific) schema
+ // object.
+ if (!$this->_schemaClass)
+ $this->_schemaClass = get_class($this).'_Schema';
+ $this->_schema = new $this->_schemaClass($this, array(
+ 'cache' => $this->_cache,
+ 'logger' => $this->_logger));
+ $this->_schemaMethods = array_flip(get_class_methods($this->_schema));
+
+ $this->connect();
+ }
+
+ /**
+ * Free any resources that are open.
+ */
+ public function __destruct()
+ {
+ $this->disconnect();
+ }
+
+
+ /*##########################################################################
+ # Object composition
+ ##########################################################################*/
+
+ /**
+ * Delegate calls to the schema object.
+ *
+ * @param string $method
+ * @param array $args
+ */
+ public function __call($method, $args)
+ {
+ if (isset($this->_schemaMethods[$method])) {
+ return call_user_func_array(array($this->_schema, $method), $args);
+ }
+
+ throw new BadMethodCallException('Call to undeclared method "'.$method.'"');
+ }
+
+
+ /*##########################################################################
+ # 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 'Abstract';
+ }
+
+ /**
+ * Does this adapter support migrations? Backend specific, as the
+ * abstract adapter always returns +false+.
+ *
+ * @return boolean
+ */
+ public function supportsMigrations()
+ {
+ return false;
+ }
+
+ /**
+ * Does this adapter support using DISTINCT within COUNT? This is +true+
+ * for all adapters except sqlite.
+ *
+ * @return boolean
+ */
+ public function supportsCountDistinct()
+ {
+ return true;
+ }
+
+ /**
+ * Should primary key values be selected from their corresponding
+ * sequence before the insert statement? If true, next_sequence_value
+ * is called before each insert to set the record's primary key.
+ * This is false for all adapters but Firebird.
+ */
+ public function prefetchPrimaryKey($tableName = null)
+ {
+ return false;
+ }
+
+ /**
+ * Reset the timer
+ *
+ * @return int
+ */
+ public function resetRuntime()
+ {
+ $runtime = $this->_runtime;
+ $this->_runtime = 0;
+ return $this->_runtime;
+ }
+
+
+ /*##########################################################################
+ # Connection Management
+ ##########################################################################*/
+
+ /**
+ * Connect to the db
+ */
+ abstract public function connect();
+
+ /**
+ * Is the connection active
+ *
+ * @return boolean
+ */
+ public function isActive()
+ {
+ return $this->_active;
+ }
+
+ /**
+ * Reconnect to the db
+ */
+ public function reconnect()
+ {
+ $this->disconnect();
+ $this->connect();
+ }
+
+ /**
+ * Disconnect from db
+ */
+ public function disconnect()
+ {
+ $this->_connection = null;
+ $this->_active = false;
+ }
+
+ /**
+ * Provides access to the underlying database connection. Useful for when
+ * you need to call a proprietary method such as postgresql's lo_* methods
+ *
+ * @return resource
+ */
+ public function rawConnection()
+ {
+ return $this->_connection;
+ }
+
+
+ /*##########################################################################
+ # Database Statements
+ ##########################################################################*/
+
+ /**
+ * Returns an array of records with the column names as keys, and
+ * column values as values.
+ *
+ * @param string $sql
+ * @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 Traversable
+ */
+ public function select($sql, $arg1=null, $arg2=null)
+ {
+ return $this->execute($sql, $arg1, $arg2);
+ }
+
+ /**
+ * Returns an array of record hashes with the column names as keys and
+ * column values as values.
+ *
+ * @param string $sql
+ * @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)
+ {
+ $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
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ * @return array
+ */
+ public function selectOne($sql, $arg1=null, $arg2=null)
+ {
+ $result = $this->selectAll($sql, $arg1, $arg2);
+ return $result ? current($result) : 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)
+ {
+ $result = $this->selectAll($sql, $arg1, $arg2);
+ foreach ($result as $row) {
+ $values[] = current($row);
+ }
+ return isset($values) ? $values : array();
+ }
+
+ /**
+ * Executes the SQL statement in the context of this connection.
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ */
+ public function execute($sql, $arg1=null, $arg2=null)
+ {
+ if (is_array($arg1)) {
+ $sql = $this->_replaceParameters($sql, $arg1);
+ $name = $arg2;
+ } else {
+ $name = $arg1;
+ }
+
+ $t = new Horde_Support_Timer;
+ $t->push();
+
+ try {
+ $stmt = $this->_connection->query($sql);
+ } catch (Exception $e) {
+ $this->_logInfo($sql, 'QUERY FAILED: ' . $e->getMessage());
+ $this->_logInfo($sql, $name);
+ throw new Horde_Db_Exception((string)$e->getMessage(), (int)$e->getCode());
+ }
+
+ $this->_logInfo($sql, $name, $t->pop());
+
+ $this->_rowCount = $stmt ? $stmt->rowCount() : 0;
+ 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->_connection->lastInsertId();
+ }
+
+ /**
+ * Executes the update statement and returns the number of rows affected.
+ *
+ * @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 update($sql, $arg1=null, $arg2=null)
+ {
+ $this->execute($sql, $arg1, $arg2);
+ return $this->_rowCount;
+ }
+
+ /**
+ * Executes the delete statement and returns the number of rows affected.
+ *
+ * @param string $sql
+ * @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 delete($sql, $arg1=null, $arg2=null)
+ {
+ $this->execute($sql, $arg1, $arg2);
+ return $this->_rowCount;
+ }
+
+ /**
+ * Check if a transaction has been started
+ */
+ public function transactionStarted()
+ {
+ return $this->_transactionStarted;
+ }
+
+ /**
+ * Begins the transaction (and turns off auto-committing).
+ */
+ public function beginDbTransaction()
+ {
+ $this->_transactionStarted = true;
+ $this->_connection->beginTransaction();
+ }
+
+ /**
+ * Commits the transaction (and turns on auto-committing).
+ */
+ public function commitDbTransaction()
+ {
+ $this->_connection->commit();
+ $this->_transactionStarted = false;
+ }
+
+ /**
+ * 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 = false;
+ }
+
+ /**
+ * Appends +LIMIT+ and +OFFSET+ options to a SQL statement.
+ *
+ * @param string $sql
+ * @param array $options
+ * @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;
+ }
+
+ public function sanitizeLimit($limit)
+ {
+ if (strpos($limit, ',') !== false) {
+ return implode(',', array_map(create_function('$i', 'return (int)$i;'), explode(',', $limit)));
+ } else return (int)$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'
+ */
+ public function addLock(&$sql, $options = array())
+ {
+ if (isset($options['lock']) && is_string($options['lock'])) {
+ $sql .= ' ' . $lock;
+ } else {
+ $sql .= ' FOR UPDATE';
+ }
+ }
+
+ /**
+ * Inserts the given fixture into the table. Overridden in adapters that
+ * require something beyond a simple insert (eg. Oracle).
+ */
+ 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');
+ }
+
+ public function emptyInsertStatement($tableName)
+ {
+ return 'INSERT INTO '.$this->quoteTableName($tableName).' VALUES(DEFAULT)';
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * Replace ? in a SQL statement with quoted values from $args
+ *
+ * @param string $sql
+ * @param array $args
+ */
+ protected function _replaceParameters($sql, $args)
+ {
+ $paramCount = substr_count($sql, '?');
+ if (count($args) != $paramCount) {
+ throw new Horde_Db_Exception('Parameter count mismatch');
+ }
+
+ $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
+ * @param string $name
+ * @param float $runtime
+ */
+ protected function _logInfo($sql, $name, $runtime=null)
+ {
+ /*@TODO */
+ $name = (empty($name) ? '' : $name)
+ . (empty($runtime) ? '' : " ($runtime ms)");
+ $this->_logger->info($this->_formatLogEntry($name, $sql));
+ }
+
+ /**
+ * Formats the log entry.
+ *
+ * @param string $message
+ * @param string $sql
+ */
+ protected function _formatLogEntry($message, $sql)
+ {
+ $sql = preg_replace("/\s+/", ' ', $sql);
+ $sql = "\n\t".wordwrap($sql, 70, "\n\t ", 1);
+ return "SQL $message $sql";
+ }
+
+}
--- /dev/null
+/ColumnDefinition.php/1.1/Fri Sep 19 03:59:03 2008//
+/TableDefinition.php/1.1/Fri Sep 19 03:59:03 2008//
+/Column.php/1.2/Wed Oct 22 01:41:15 2008//
+/Schema.php/1.2/Wed Oct 22 01:41:15 2008//
+D
--- /dev/null
+framework/Db/lib/Horde/Db/Adapter/Abstract
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Abstract_Column
+{
+ protected $_name;
+ protected $_type;
+ protected $_null;
+ protected $_primary;
+ protected $_limit;
+ protected $_precision;
+ protected $_scale;
+ protected $_default;
+ protected $_sqlType;
+ protected $_isText;
+ protected $_isNumber;
+
+
+ /*##########################################################################
+ # Construct/Destruct
+ ##########################################################################*/
+
+ /**
+ * Construct
+ *
+ * @param string $name The column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
+ * @param string $default The type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
+ * @param string $sqlType Only used to extract the column's length, if necessary. For example +60+ in <tt>company_name varchar(60)</tt>.
+ * @param boolean $null Determines if 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->_type = $this->_simplifiedType($sqlType);
+ $this->_default = $this->extractDefault($default);
+ $this->_primary = false;
+
+ $this->_isText = $this->_type == 'text' || $this->_type == 'string';
+ $this->_isNumber = $this->_type == 'float' || $this->_type == 'integer' || $this->_type == 'decimal';
+ }
+
+ /**
+ * @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 isNull()
+ {
+ return $this->_null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSqlType()
+ {
+ return $this->_sqlType;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function isPrimary()
+ {
+ return $this->_primary;
+ }
+
+ /**
+ * @param boolean
+ */
+ public function setPrimary($primary)
+ {
+ $this->_primary = $primary;
+ }
+
+
+ /*##########################################################################
+ # Type Juggling
+ ##########################################################################*/
+
+ /**
+ * Used to convert from Strings to BLOBs
+ *
+ * @return string
+ */
+ public function stringToBinary($value)
+ {
+ return $value;
+ }
+
+ /**
+ * Used to convert from BLOBs to Strings
+ *
+ * @return string
+ */
+ public function binaryToString($value)
+ {
+ return $value;
+ }
+
+ /**
+ * @TODO Return a Horde_Date or DateTime object instead?
+ *
+ * @param string $string
+ * @return string
+ */
+ public function stringToDate($string)
+ {
+ if (empty($string)) { return null; }
+
+ // preserve '0000-00-00' (http://bugs.php.net/bug.php?id=45647)
+ if (preg_replace('/[^\d]/', '', $string) == 0) {
+ return '0000-00-00';
+ }
+
+ return date('Y-m-d', strtotime($string));
+ }
+
+ /**
+ * @TODO Return a Horde_Date or DateTime object instead?
+ *
+ * @param string $string
+ * @return string
+ */
+ public function stringToTime($string)
+ {
+ if (empty($string)) { return null; }
+
+ // preserve '0000-00-00 00:00:00' (http://bugs.php.net/bug.php?id=45647)
+ if (preg_replace('/[^\d]/', '', $string) == 0) {
+ return '0000-00-00 00:00:00';
+ }
+
+ return date('Y-m-d H:i:s', strtotime($string));
+ }
+
+ /**
+ * @TODO Return a Horde_Date or DateTime object instead?
+ *
+ * @param string $string
+ * @return string
+ */
+ public function stringToDummyTime($value)
+ {
+ if (empty($string)) return null;
+ return $this->stringToTime('2000-01-01 ' . $string);
+ }
+
+ /**
+ * @param mixed $value
+ * @return boolean
+ */
+ public function valueToBoolean($value)
+ {
+ if ($value === true || $value === false) {
+ return $value;
+ }
+
+ $value = strtolower($value);
+ return $value == 'true' || $value == 't' || $value == '1';
+ }
+
+ /**
+ * @param mixed $value
+ * @return decimal
+ */
+ public function valueToDecimal($value)
+ {
+ return (float)$value;
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * @param string $sqlType
+ * @return int
+ */
+ protected function _extractLimit($sqlType)
+ {
+ if (preg_match("/\((.*)\)/", $sqlType, $matches)) {
+ return (int)$matches[1];
+ }
+ return null;
+ }
+
+ /**
+ * @param string $sqlType
+ * @return int
+ */
+ protected function _extractPrecision($sqlType)
+ {
+ if (preg_match("/^(numeric|decimal|number)\((\d+)(,\d+)?\)/i", $sqlType, $matches)) {
+ return (int)$matches[2];
+ }
+ return null;
+ }
+
+ /**
+ * @param string $sqlType
+ * @return int
+ */
+ protected function _extractScale($sqlType)
+ {
+ switch (true) {
+ case preg_match("/^(numeric|decimal|number)\((\d+)\)/i", $sqlType):
+ return 0;
+ case preg_match("/^(numeric|decimal|number)\((\d+)(,(\d+))\)/i",
+ $sqlType, $match):
+ return (int)$match[4];
+ }
+ }
+
+ /**
+ * @param string $fieldType
+ * @return string
+ */
+ protected function _simplifiedType($fieldType)
+ {
+ switch (true) {
+ case preg_match('/int/i', $fieldType):
+ return 'integer';
+ case preg_match('/float|double/i', $fieldType):
+ return 'float';
+ case preg_match('/decimal|numeric|number/i', $fieldType):
+ return $this->_scale == 0 ? 'integer' : 'decimal';
+ case preg_match('/datetime/i', $fieldType):
+ return 'datetime';
+ case preg_match('/timestamp/i', $fieldType):
+ return 'timestamp';
+ case preg_match('/time/i', $fieldType):
+ return 'time';
+ case preg_match('/date/i', $fieldType):
+ return 'date';
+ case preg_match('/clob|text/i', $fieldType):
+ return 'text';
+ case preg_match('/blob|binary/i', $fieldType):
+ return 'binary';
+ case preg_match('/char|string/i', $fieldType):
+ return 'string';
+ case preg_match('/boolean/i', $fieldType):
+ return 'boolean';
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Abstract_ColumnDefinition
+{
+ protected $_base = null;
+ protected $_name = null;
+ protected $_type = null;
+ protected $_limit = null;
+ protected $_precision = null;
+ protected $_scale = null;
+ protected $_default = null;
+ protected $_null = null;
+
+ /**
+ * Construct
+ */
+ public function __construct($base, $name, $type, $limit=null,
+ $precision=null, $scale=null, $default=null, $null=null)
+ {
+ // protected
+ $this->_base = $base;
+
+ // public
+ $this->_name = $name;
+ $this->_type = $type;
+ $this->_limit = $limit;
+ $this->_precision = $precision;
+ $this->_scale = $scale;
+ $this->_default = $default;
+ $this->_null = $null;
+ }
+
+ /*##########################################################################
+ # Public
+ ##########################################################################*/
+
+ /**
+ * @return string
+ */
+ public function toSql()
+ {
+ $sql = $this->_base->quoteColumnName($this->_name).' ';
+ try {
+ $sql .= $this->_base->typeToSql($this->_type, $this->_limit,
+ $this->_precision, $this->_scale);
+ } catch (Exception $e) {
+ $sql .= $this->_type;
+ }
+ 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);
+ } 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 isNull()
+ {
+ return $this->_null;
+ }
+
+ /**
+ * @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 setNull($null)
+ {
+ $this->_null = $null;
+ }
+
+
+ /*##########################################################################
+ # Schema Statements
+ ##########################################################################*/
+
+ /**
+ * @param string $sql
+ * @param array $options
+ */
+ protected function _addColumnOptions($sql, $options)
+ {
+ return $this->_base->addColumnOptions($sql,
+ array_merge($options, array('column' => $this))
+ );
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+abstract class Horde_Db_Adapter_Abstract_Schema
+{
+ /**
+ * @var Cache object
+ */
+ protected $_cache = null;
+
+ /**
+ * @var Logger
+ */
+ protected $_logger = null;
+
+ /**
+ * @var Horde_Db_Adapter_Abstract
+ */
+ protected $_adapter = null;
+
+ /**
+ * @var array
+ */
+ protected $_adapterMethods = array();
+
+
+ /*##########################################################################
+ # Construct/Destruct
+ ##########################################################################*/
+
+ /**
+ * @param Horde_Db_Adapter_Abstract $adapter
+ * @param array $config
+ */
+ public function __construct($adapter, $config = array())
+ {
+ $this->_adapter = $adapter;
+ $this->_adapterMethods = array_flip(get_class_methods($adapter));
+
+ $this->_cache = isset($config['cache']) ? $config['cache'] : new Horde_Support_Stub;
+ $this->_logger = isset($config['logger']) ? $config['logger'] : new Horde_Support_Stub;
+ }
+
+
+ /*##########################################################################
+ # Object composition
+ ##########################################################################*/
+
+ /**
+ * Delegate calls to the adapter object.
+ *
+ * @param string $method
+ * @param array $args
+ */
+ 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.'"');
+ }
+
+
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ /**
+ * Quotes the column value to help prevent
+ * {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
+ *
+ * @param string $value
+ * @param string $column
+ * @return string
+ */
+ public function quote($value, $column=null)
+ {
+ if (is_object($value) && is_callable(array($value, 'quotedId'))) {
+ return $value->quotedId();
+ }
+
+ $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_int($value) || is_float($value)) {
+ return $value;
+ /*@TODO
+ else
+ if value.acts_like?(:date) || value.acts_like?(:time)
+ "'#{quoted_date(value)}'"
+ else
+ "#{quoted_string_prefix}'#{quote_string(value.to_yaml)}'"
+ end
+ */
+ } else {
+ /*@TODO
+ when String, ActiveSupport::Multibyte::Chars
+ value = value.to_s
+ if column && column.type == :binary && column.class.respond_to?(:string_to_binary)
+ "#{quoted_string_prefix}'#{quote_string(column.class.string_to_binary(value))}'" # ' (for ruby-mode)
+ elsif column && [:integer, :float].include?(column.type)
+ value = column.type == :integer ? value.to_i : value.to_f
+ value.to_s
+ else
+ "#{quoted_string_prefix}'#{quote_string(value)}'" # ' (for ruby-mode)
+ end
+ */
+ return $this->_adapter->quoteString($value);
+ }
+ }
+
+ /**
+ * Quotes a string, escaping any ' (single quote) and \ (backslash)
+ * characters..
+ *
+ * @param string $string
+ * @return string
+ */
+ public function quoteString($string)
+ {
+ return "'".str_replace(array('\\', '\''), array('\\\\', '\\\''), $string)."'";
+ }
+
+ /**
+ * Returns a quoted form of the column name. This is highly adapter
+ * specific.
+ *
+ * @param string $name
+ * @return string
+ */
+ abstract public function quoteColumnName($name);
+
+ /**
+ * Returns a quoted form of the table name. Defaults to column name quoting.
+ *
+ * @param string $name
+ * @return string
+ */
+ public function quoteTableName($name)
+ {
+ return $this->quoteColumnName($name);
+ }
+
+ /**
+ * @return string
+ */
+ public function quoteTrue()
+ {
+ return "'t'";
+ }
+
+ /**
+ * @return string
+ */
+ public function quoteFalse()
+ {
+ return "'f'";
+ }
+
+ /**
+ * @return string
+ */
+ public function quoteDate($value)
+ {
+ return $this->_adapter->quoteString((string)$value);
+ }
+
+ /**
+ * @return string
+ */
+ public function quotedStringPrefix()
+ {
+ return '';
+ }
+
+
+ /*##########################################################################
+ # 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.
+ *
+ * @return array
+ */
+ public function nativeDatabaseTypes()
+ {
+ return array();
+ }
+
+ /**
+ * This is the maximum length a table alias can be
+ *
+ * @return int
+ */
+ public function tableAliasLength()
+ {
+ return 255;
+ }
+
+ /**
+ * Truncates a table alias according to the limits of the current adapter.
+ *
+ * @param string $tableName
+ * @return string
+ */
+ public function tableAliasFor($tableName)
+ {
+ $alias = substr($tableName, 0, $this->tableAliasLength());
+ return str_replace('.', '_', $alias);
+ }
+
+ /**
+ * @param string $name
+ * @return array
+ */
+ abstract public function tables($name=null);
+
+ /**
+ * Returns an array of indexes for the given table.
+ *
+ * @param string $tableName
+ * @param string $name
+ * @return array
+ */
+ abstract public function indexes($tableName, $name=null);
+
+ /**
+ * Returns an array of Column objects for the table specified by +table_name+.
+ * See the concrete implementation for details on the expected parameter values.
+ *
+ * @param string $tableName
+ * @param string $name
+ * @return array
+ */
+ abstract public function columns($tableName, $name=null);
+
+ /**
+ * Creates a new table
+ * There are two ways to work with #create_table. You can use the block
+ * form or the regular form, like this:
+ *
+ * === Block form
+ * # create_table() yields a TableDefinition instance
+ * create_table(:suppliers) do |t|
+ * t.column :name, :string, :limit => 60
+ * # Other fields here
+ * end
+ *
+ * === Regular form
+ * create_table(:suppliers)
+ * add_column(:suppliers, :name, :string, {:limit => 60})
+ *
+ * The +options+ hash can include the following keys:
+ * [<tt>:id</tt>]
+ * Set to true or false to add/not add a primary key column
+ * automatically. Defaults to true.
+ * [<tt>:primary_key</tt>]
+ * The name of the primary key, if one is to be added automatically.
+ * Defaults to +id+.
+ * [<tt>:options</tt>]
+ * Any extra options you want appended to the table definition.
+ * [<tt>:temporary</tt>]
+ * Make a temporary table.
+ * [<tt>:force</tt>]
+ * 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)
+ * create_table(:suppliers, :options => 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
+ * generates:
+ * CREATE TABLE suppliers (
+ * id int(11) DEFAULT NULL auto_increment PRIMARY KEY
+ * ) ENGINE=InnoDB DEFAULT CHARSET=utf8
+ *
+ * ====== Rename the primary key column
+ * create_table(:objects, :primary_key => 'guid') do |t|
+ * t.column :name, :string, :limit => 80
+ * end
+ * generates:
+ * CREATE TABLE objects (
+ * guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
+ * name varchar(80)
+ * )
+ *
+ * ====== Do not add a primary key column
+ * create_table(:categories_suppliers, :id => false) do |t|
+ * t.column :category_id, :integer
+ * t.column :supplier_id, :integer
+ * end
+ * generates:
+ * CREATE TABLE categories_suppliers_join (
+ * category_id int,
+ * supplier_id int
+ * )
+ *
+ * See also TableDefinition#column for details on how to create columns.
+ *
+ * @param string $name
+ * @param array $options
+ */
+ public function createTable($name, $options=array())
+ {
+ $pk = isset($options['primaryKey']) &&
+ $options['primaryKey'] === false ? false : 'id';
+ $tableDefinition =
+ new Horde_Db_Adapter_Abstract_TableDefinition($name, $this, $options);
+ if ($pk != false) {
+ $tableDefinition->primaryKey($pk);
+ }
+ return $tableDefinition;
+ }
+
+ /**
+ * Execute table creation
+ *
+ * @param string $name
+ * @param array $options
+ */
+ public function endTable($name, $options=array())
+ {
+ if ($name instanceof Horde_Db_Adapter_Abstract_TableDefinition) {
+ $tableDefinition = $name;
+ $options = array_merge($tableDefinition->getOptions(), $options);
+ } else {
+ $tableDefinition = $this->createTable($name, $options);
+ }
+
+ // drop previous
+ if (isset($options['force'])) {
+ $this->dropTable($tableDefinition->getName(), $options);
+ }
+
+ $temp = !empty($options['temporary']) ? 'TEMPORARY' : null;
+ $opts = !empty($options['options']) ? $options['options'] : null;
+
+ $sql = "CREATE $temp TABLE ".$this->quoteTableName($tableDefinition->getName())." (\n".
+ $tableDefinition->toSql()."\n".
+ ") $opts";
+ return $this->execute($sql);
+ }
+
+ /**
+ * Renames a table.
+ * ===== Example
+ * rename_table('octopuses', 'octopi')
+ *
+ * @param string $name
+ * @param string $newName
+ */
+ abstract public function renameTable($name, $newName);
+
+ /**
+ * Drops a table from the database.
+ *
+ * @param string $name
+ */
+ public function dropTable($name)
+ {
+ $this->_clearTableCache($name);
+ return $this->execute('DROP TABLE ' . $this->quoteTableName($name));
+ }
+
+ /**
+ * Adds a new column to the named table.
+ * See TableDefinition#column for details of the options you can use.
+ *
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $type
+ * @param array $options
+ */
+ public function addColumn($tableName, $columnName, $type, $options=array())
+ {
+ $this->_clearTableCache($tableName);
+
+ $limit = isset($options['limit']) ? $options['limit'] : null;
+ $precision = isset($options['precision']) ? $options['precision'] : null;
+ $scale = isset($options['scale']) ? $options['scale'] : null;
+
+ $sql = 'ALTER TABLE ' . $this->quoteTableName($tableName) .
+ ' ADD '.$this->quoteColumnName($columnName) .
+ ' '.$this->typeToSql($type, $limit, $precision, $scale);
+ $sql = $this->addColumnOptions($sql, $options);
+ return $this->execute($sql);
+ }
+
+ /**
+ * Removes the column from the table definition.
+ * ===== Examples
+ * remove_column(:suppliers, :qualification)
+ *
+ * @param string $tableName
+ * @param string $columnName
+ */
+ public function removeColumn($tableName, $columnName)
+ {
+ $this->_clearTableCache($tableName);
+
+ $sql = 'ALTER TABLE ' . $this->quoteTableName($tableName).' DROP '.$this->quoteColumnName($columnName);
+ return $this->execute($sql);
+ }
+
+ /**
+ * Changes the column's definition according to the new options.
+ * See TableDefinition#column for details of the options you can use.
+ * ===== Examples
+ * change_column(:suppliers, :name, :string, :limit => 80)
+ * change_column(:accounts, :description, :text)
+ *
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $type
+ * @param array $options
+ */
+ 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
+ * DatabaseStatements#execute the apppropriate SQL statement yourself.
+ * ===== Examples
+ * change_column_default(:suppliers, :qualification, 'new')
+ * change_column_default(:accounts, :authorized, 1)
+ *
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $default
+ */
+ abstract public function changeColumnDefault($tableName, $columnName, $default);
+
+ /**
+ * Renames a column.
+ * ===== Example
+ * rename_column(:suppliers, :description, :name)
+ *
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $newColumnName
+ */
+ abstract public function renameColumn($tableName, $columnName, $newColumnName);
+
+ /**
+ * Adds a new index to the table. +column_name+ can be a single Symbol, or
+ * an Array of Symbols.
+ *
+ * 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+, +: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
+ * add_index(:suppliers, :name)
+ * generates
+ * CREATE INDEX suppliers_name_index ON suppliers(name)
+ *
+ * ====== Creating a unique index
+ * add_index(:accounts, [:branch_id, :party_id], :unique => true)
+ * generates
+ * CREATE UNIQUE INDEX accounts_branch_id_index ON accounts(branch_id, party_id)
+ *
+ * ====== Creating a named index
+ * add_index(:accounts, [:branch_id, :party_id], :unique => true, :name => 'by_branch_party')
+ * generates
+ * CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
+ *
+ * @param string $tableName
+ * @param string $columnName
+ * @param array $options
+ */
+ public function addIndex($tableName, $columnName, $options=array())
+ {
+ $this->_clearTableCache($tableName);
+
+ $columnNames = (array)($columnName);
+ $indexName = $this->indexName($tableName, array('column' => $columnNames));
+
+ $indexType = !empty($options['unique']) ? "UNIQUE" : null;
+ $indexName = !empty($options['name']) ? $options['name'] : $indexName;
+
+ foreach ($columnNames as $colName) {
+ $quotedCols[] = $this->quoteColumnName($colName);
+ }
+ $quotedColumnNames = implode(', ', $quotedCols);
+ $sql = "CREATE $indexType INDEX ".$this->quoteColumnName($indexName).
+ 'ON '.$this->quoteTableName($tableName) . " ($quotedColumnNames)";
+ return $this->execute($sql);
+ }
+
+ /**
+ * Remove the given index from the table.
+ *
+ * Remove the suppliers_name_index in the suppliers table (legacy support, use the second or third forms).
+ * remove_index :suppliers, :name
+ * Remove the index named accounts_branch_id in the accounts table.
+ * remove_index :accounts, :column => :branch_id
+ * Remove the index named by_branch_party in the accounts table.
+ * remove_index :accounts, :name => :by_branch_party
+ *
+ * You can remove an index on multiple columns by specifying the first column.
+ * add_index :accounts, [:username, :password]
+ * remove_index :accounts, :username
+ *
+ * @param string $tableName
+ * @param array $options
+ */
+ public function removeIndex($tableName, $options=array())
+ {
+ $this->_clearTableCache($tableName);
+
+ $index = $this->indexName($tableName, $options);
+ $sql = "DROP INDEX ".$this->quoteColumnName($index).' ON ' . $this->quoteTableName($tableName);
+ return $this->execute($sql);
+ }
+
+ /**
+ * Get the name of the index
+ *
+ * @param string $tableName
+ * @param array $options
+ */
+ 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);
+
+ } elseif (isset($options['name'])) {
+ return $options['name'];
+
+ } else {
+ throw new Horde_Db_Exception('You must specify the index name');
+ }
+ }
+
+ /**
+ * Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
+ * entire structure of the database.
+ *
+ * @param string $table
+ * @return string
+ */
+ abstract public function structureDump($table=null);
+
+ /**
+ * Recreate the given db
+ *
+ * @param string $name
+ */
+ public function recreateDatabase($name)
+ {
+ $this->dropDatabase($name);
+ return $this->createDatabase($name);
+ }
+
+ /**
+ * Create the given db
+ *
+ * @param string $name
+ */
+ abstract public function createDatabase($name);
+
+ /**
+ * Drop the given db
+ *
+ * @param string $name
+ */
+ abstract public function dropDatabase($name);
+
+ /**
+ * Get the name of the current db
+ *
+ * @return string
+ */
+ abstract public function currentDatabase();
+
+ /**
+ * Should not be called normally, but this operation is non-destructive.
+ * The migrations module handles this automatically.
+ */
+ public function initializeSchemaInformation()
+ {
+ try {
+ $this->execute("CREATE TABLE schema_info (".
+ " version ".$this->typeToSql('integer').
+ ")");
+ return $this->execute("INSERT INTO schema_info (version) VALUES (0)");
+ } catch (Exception $e) {}
+ }
+
+ /**
+ * The sql for this column type
+ *
+ * @param string $type
+ * @param string $limit
+ */
+ public function typeToSql($type, $limit=null, $precision=null, $scale=null)
+ {
+ $natives = $this->nativeDatabaseTypes();
+ $native = isset($natives[$type]) ? $natives[$type] : null;
+ if (empty($native)) { return $type; }
+
+ $sql = is_array($native) ? $native['name'] : $native;
+ if ($type == 'decimal') {
+ $nativePrec = isset($native['precision']) ? $native['precision'] : null;
+ $nativeScale = isset($native['scale']) ? $native['scale'] : null;
+
+ $precision = !empty($precision) ? $precision : $nativePrec;
+ $scale = !empty($scale) ? $scale : $nativeScale;
+ if ($precision) {
+ $sql .= $scale ? "($precision, $scale)" : "($precision)";
+ }
+ } else {
+ $nativeLimit = is_array($native) ? $native['limit'] : null;
+ if ($limit = !empty($limit) ? $limit : $nativeLimit) {
+ $sql .= "($limit)";
+ }
+ }
+ return $sql;
+ }
+
+ /**
+ * Add default/null options to column sql
+ *
+ * @param string $sql
+ * @param array $options
+ */
+ public function addColumnOptions($sql, $options)
+ {
+ 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;
+ }
+
+ /**
+ * SELECT DISTINCT clause for a given set of columns and a given
+ * ORDER BY clause. Both PostgreSQL and Oracle override this for
+ * custom DISTINCT syntax.
+ *
+ * $connection->distinct("posts.id", "posts.created_at desc")
+ *
+ * @param string $columns
+ * @param string $orderBy
+ */
+ public function distinct($columns, $orderBy=null)
+ {
+ return "DISTINCT $columns";
+ }
+
+ /**
+ * ORDER BY clause for the passed order option.
+ * PostgreSQL overrides this due to its stricter standards compliance.
+ *
+ * @param string $sql
+ * @param array $options
+ * @return string
+ */
+ public function addOrderByForAssocLimiting($sql, $options)
+ {
+ return $sql.'ORDER BY '.$options['order'];
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * We need to clear cache for tables when altering them at all
+ */
+ protected function _clearTableCache($tableName)
+ {
+ $this->_cache->set("tables/$tableName", null);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Abstract_TableDefinition implements ArrayAccess
+{
+ protected $_name = null;
+ protected $_base = null;
+ protected $_options = null;
+ protected $_columns = null;
+
+ /**
+ * Class Constructor
+ *
+ * @param string $name
+ * @param Horde_Db_Adapter_Abstract_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)
+ {
+ $natives = $this->_native();
+ $this->column($name, $natives['primaryKey']);
+ }
+
+ /**
+ * Instantiates a new column for the table.
+ * The +type+ parameter must be one of the following values:
+ * <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
+ * <tt>:integer</tt>, <tt>:float</tt>, <tt>:datetime</tt>,
+ * <tt>:timestamp</tt>, <tt>:time</tt>, <tt>:date</tt>,
+ * <tt>:binary</tt>, <tt>:boolean</tt>.
+ *
+ * Available options are (none of these exists by default):
+ * * <tt>:limit</tt>:
+ * Requests a maximum column length (<tt>:string</tt>, <tt>:text</tt>,
+ * <tt>:binary</tt> or <tt>:integer</tt> columns only)
+ * * <tt>:default</tt>:
+ * The column's default value. You cannot explicitely set the default
+ * value to +NULL+. Simply leave off this option if you want a +NULL+
+ * default value.
+ * * <tt>:null</tt>:
+ * Allows or disallows +NULL+ values in the column. This option could
+ * have been named <tt>:null_allowed</tt>.
+ *
+ * This method returns <tt>self</tt>.
+ *
+ * ===== Examples
+ * # Assuming def is an instance of TableDefinition
+ * def.column(:granted, :boolean)
+ * #=> granted BOOLEAN
+ *
+ * def.column(:picture, :binary, :limit => 2.megabytes)
+ * #=> picture BLOB(2097152)
+ *
+ * def.column(:sales_stage, :string, :limit => 20, :default => 'new', :null => false)
+ * #=> sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
+ *
+ * @return TableDefinition
+ */
+ public function column($name, $type, $options=array())
+ {
+ if ($this[$name]) {
+ $column = $this[$name];
+ } else {
+ $column = new Horde_Db_Adapter_Abstract_ColumnDefinition(
+ $this->_base, $name, $type);
+ }
+
+ $natives = $this->_native();
+ $opt = $options;
+
+ if (isset($opt['limit']) || isset($natives[$type])) {
+ $nativeLimit = isset($natives[$type]['limit']) ? $natives[$type]['limit'] : null;
+ $column->setLimit(isset($opt['limit']) ? $opt['limit'] : $nativeLimit);
+ }
+
+ $column->setPrecision(isset($opt['precision']) ? $opt['precision'] : null);
+ $column->setScale(isset($opt['scale']) ? $opt['scale'] : null);
+ $column->setDefault(isset($opt['default']) ? $opt['default'] : null);
+ $column->setNull(isset($opt['null']) ? $opt['null'] : null);
+
+ $this[$name] ? $this[$name] = $column : $this->_columns[] = $column;
+ return $this;
+ }
+
+ /**
+ * Wrap up table creation block & create the table
+ */
+ public function end()
+ {
+ return $this->_base->endTable($this);
+ }
+
+ /**
+ * Returns a String whose contents are the column definitions
+ * concatenated together. This string can then be pre and appended to
+ * to generate the final SQL to create the table.
+ *
+ * @return string
+ */
+ public function toSql()
+ {
+ $cols = array();
+ foreach ($this->_columns as $col) { $cols[] = $col->toSql(); }
+
+ return " ".implode(", \n ", $cols);
+ }
+
+
+ /*##########################################################################
+ # 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_Abstract_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]);
+ }
+ }
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * Get the types
+ */
+ protected function _native()
+ {
+ return $this->_base->nativeDatabaseTypes();
+ }
+
+}
--- /dev/null
+D/Abstract////
+D/Mssql////
+D/Mysql////
+D/Mysqli////
+D/Pdo////
+D/Postgresql////
+D/Sqlite////
+D/Oracle////
+/Abstract.php/1.1/Fri Sep 19 03:59:03 2008//
+/Oci8.php/1.1/Fri Sep 19 03:59:03 2008//
+/Mysqli.php/1.2/Fri Sep 26 12:36:31 2008//
--- /dev/null
+framework/Db/lib/Horde/Db/Adapter
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+/Schema.php/1.1/Fri Sep 19 03:59:03 2008//
+D
--- /dev/null
+framework/Db/lib/Horde/Db/Adapter/Mssql
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2006-2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Mssql_Schema extends Horde_Db_Adapter_Abstract_Schema
+{
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ /**
+ * @return string
+ */
+ public function quoteColumnName($name)
+ {
+ return '[' . str_replace(']', ']]', $name) . ']';
+ }
+
+
+ /**
+ */
+ public function getTables()
+ {
+ return 'SELECT name FROM sysobjects WHERE type = \'U\' ORDER BY name';
+ }
+
+ /**
+ */
+ protected function _limit($query, &$sql, &$bindParams)
+ {
+ if ($query->limit) {
+ $orderby = stristr($sql, 'ORDER BY');
+ if ($orderby !== false) {
+ $sort = (stripos($orderby, 'DESC') !== false) ? 'DESC' : 'ASC';
+ $order = str_ireplace('ORDER BY', '', $orderby);
+ $order = trim(preg_replace('/ASC|DESC/i', '', $order));
+ }
+
+ $sql = preg_replace('/^SELECT /i', 'SELECT TOP ' . ($query->limit + $query->limitOffset) . ' ', $sql);
+
+ $sql = 'SELECT * FROM (SELECT TOP ' . $query->limit . ' * FROM (' . $sql . ') AS inner_tbl';
+ if ($orderby !== false) {
+ $sql .= ' ORDER BY ' . $order . ' ';
+ $sql .= (stripos($sort, 'ASC') !== false) ? 'DESC' : 'ASC';
+ }
+ $sql .= ') AS outer_tbl';
+ if ($orderby !== false) {
+ $sql .= ' ORDER BY ' . $order . ' ' . $sort;
+ }
+ }
+ }
+
+ /**
+ * Get a description of the database table that $model is going to
+ * reflect.
+ */
+ public function loadModel($model)
+ {
+ $tblinfo = $this->select('EXEC sp_columns @table_name = ' . $this->dml->quoteColumnName($model->table));
+ while ($col = $tblinfo->fetch()) {
+ if (strpos($col['type_name'], ' ') !== false) {
+ list($type, $identity) = explode(' ', $col['type_name']);
+ } else {
+ $type = $col['type_name'];
+ $identity = '';
+ }
+
+ $model->addField($col['column_name'], array('type' => $type,
+ 'null' => !(bool)$col['is_nullable'] == 'NO',
+ 'default' => $col['column_def']));
+ if (strtolower($identity) == 'identity') {
+ $model->key = $col['column_name'];
+ }
+ }
+ }
+
+ /**
+ */
+ protected function _lastInsertId($sequence)
+ {
+ return $this->selectOne('SELECT @@IDENTITY');
+ }
+
+}
--- /dev/null
+/Column.php/1.1/Fri Sep 19 03:59:03 2008//
+/Schema.php/1.1/Fri Sep 19 03:59:03 2008//
+/TableDefinition.php/1.1/Fri Sep 19 03:59:03 2008//
+D
--- /dev/null
+framework/Db/lib/Horde/Db/Adapter/Mysql
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Mysql_Column extends Horde_Db_Adapter_Abstract_Column
+{
+ /**
+ * @var array
+ */
+ protected static $_hasEmptyStringDefault = array('binary', 'string', 'text');
+
+ /**
+ * @var string
+ */
+ protected $_originalDefault = null;
+
+ /**
+ * Construct
+ * @param string $name
+ * @param string $default
+ * @param string $sqlType
+ * @param boolean $null
+ */
+ public function __construct($name, $default, $sqlType=null, $null=true)
+ {
+ $this->_originalDefault = $default;
+ parent::__construct($name, $default, $sqlType, $null);
+
+ if ($this->_isMissingDefaultForgedAsEmptyString()) {
+ $this->_default = null;
+ }
+ }
+
+ /**
+ * @param string $fieldType
+ * @return string
+ */
+ protected function _simplifiedType($fieldType)
+ {
+ if (strpos(strtolower($fieldType), 'tinyint(1)') !== false) {
+ return 'boolean';
+ } elseif (preg_match('/enum/i', $fieldType)) {
+ return 'string';
+ }
+ return parent::_simplifiedType($fieldType);
+ }
+
+ /**
+ * MySQL misreports NOT NULL column default when none is given.
+ * We can't detect this for columns which may have a legitimate ''
+ * default (string, text, binary) but we can for others (integer,
+ * datetime, boolean, and the rest).
+ *
+ * Test whether the column has default '', is not null, and is not
+ * a type allowing default ''.
+ *
+ * @return boolean
+ */
+ protected function _isMissingDefaultForgedAsEmptyString()
+ {
+ return !$this->_null && $this->_originalDefault == '' &&
+ !in_array($this->_type, self::$_hasEmptyStringDefault);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Mysql_Schema extends Horde_Db_Adapter_Abstract_Schema
+{
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ /**
+ * @return string
+ */
+ public function quoteColumnName($name)
+ {
+ return '`' . str_replace('`', '``', $name) . '`';
+ }
+
+ /**
+ * @return string
+ */
+ public function quoteTableName($name)
+ {
+ return str_replace('.', '`.`', $this->quoteColumnName($name));
+ }
+
+ /**
+ * @return string
+ */
+ public function quoteTrue()
+ {
+ return '1';
+ }
+
+ /**
+ * @return string
+ */
+ public function quoteFalse()
+ {
+ return '0';
+ }
+
+
+ /*##########################################################################
+ # Schema Statements
+ ##########################################################################*/
+
+ /**
+ * The db column types for this adapter
+ *
+ * @return array
+ */
+ public function nativeDatabaseTypes()
+ {
+ return array(
+ 'primaryKey' => 'int(11) DEFAULT NULL auto_increment PRIMARY KEY',
+ 'string' => array('name' => 'varchar', 'limit' => 255),
+ 'text' => array('name' => 'text', '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' => 'blob', 'limit' => null),
+ 'boolean' => array('name' => 'tinyint', 'limit' => 1),
+ );
+ }
+
+ /**
+ * Dump entire schema structure or specific table
+ *
+ * @param string $table
+ * @return string
+ */
+ public function structureDump($table=null)
+ {
+ foreach ($this->selectAll('SHOW TABLES') as $row) {
+ if ($table && $table != current($row)) { continue; }
+ $dump = $this->selectOne('SHOW CREATE TABLE ' . $this->quoteTableName(current($row)));
+ $creates[] = $dump['Create Table'] . ';';
+ }
+ return isset($creates) ? implode("\n\n", $creates) : null;
+ }
+
+ /**
+ * Create the given db
+ *
+ * @param string $name
+ */
+ public function createDatabase($name)
+ {
+ return $this->execute("CREATE DATABASE `$name`");
+ }
+
+ /**
+ * Drop the given db
+ *
+ * @param string $name
+ */
+ public function dropDatabase($name)
+ {
+ return $this->execute("DROP DATABASE IF EXISTS `$name`");
+ }
+
+ /**
+ * Get the name of the current db
+ *
+ * @return string
+ */
+ public function currentDatabase()
+ {
+ return $this->selectValue('SELECT DATABASE() AS db');
+ }
+
+ /**
+ * Returns the database character set
+ *
+ * @return string
+ */
+ public function getCharset()
+ {
+ return $this->showVariable('character_set_database');
+ }
+
+ /**
+ * Returns the database collation strategy
+ *
+ * @return string
+ */
+ public function getCollation()
+ {
+ return $this->showVariable('collation_database');
+ }
+
+ /**
+ * List of tables for the db
+ *
+ * @param string $name
+ */
+ public function tables($name=null)
+ {
+ return $this->selectValues('SHOW TABLES');
+ }
+
+ /**
+ * List of indexes for the given table
+ *
+ * @param string $tableName
+ * @param string $name
+ */
+ public function indexes($tableName, $name=null)
+ {
+ $indexes = array();
+ $currentIndex = null;
+ foreach ($this->select('SHOW KEYS FROM ' . $this->quoteTableName($tableName)) as $row) {
+ if ($currentIndex != $row[2]) {
+ if ($row[2] == 'PRIMARY') continue;
+ $currentIndex = $row[2];
+ $indexes[] = (object)array('table' => $row[0],
+ 'name' => $row[2],
+ 'unique' => $row[1] == '0',
+ 'columns' => array());
+ }
+ $indexes[sizeof($indexes)-1]->columns[] = $row[4];
+ }
+ return $indexes;
+ }
+
+ /**
+ * @param string $tableName
+ * @param string $name
+ */
+ public function columns($tableName, $name=null)
+ {
+ // check cache
+ $rows = @unserialize($this->_cache->get("tables/$tableName"));
+
+ // query to build rows
+ if (!$rows) {
+ $rows = $this->selectAll('SHOW FIELDS FROM ' . $this->quoteTableName($tableName), $name);
+
+ // write cache
+ $this->_cache->set("tables/$tableName", serialize($rows));
+ }
+
+ // create columns from rows
+ $columns = array();
+ foreach ($rows as $row) {
+ $columns[] = new Horde_Db_Adapter_Mysql_Column(
+ $row[0], $row[4], $row[1], $row[2] == 'YES');
+ }
+ return $columns;
+ }
+
+ /**
+ * Override createTable to return a Mysql Table Definition
+ * param string $name
+ * param array $options
+ */
+ public function createTable($name, $options=array())
+ {
+ $pk = isset($options['primaryKey']) && $options['primaryKey'] === false ? false : 'id';
+ $tableDefinition =
+ new Horde_Db_Adapter_Mysql_TableDefinition($name, $this, $options);
+ if ($pk != false) {
+ $tableDefinition->primaryKey($pk);
+ }
+ return $tableDefinition;
+ }
+
+ /**
+ * @param string $name
+ * @param array $options
+ */
+ public function endTable($name, $options=array())
+ {
+ $inno = array('options' => 'ENGINE=InnoDB');
+ return parent::endTable($name, array_merge($inno, $options));
+ }
+
+ /**
+ * @param string $name
+ * @param string $newName
+ */
+ public function renameTable($name, $newName)
+ {
+ $this->_clearTableCache($name);
+
+ return $this->execute('RENAME TABLE '.$this->quoteTableName($name).' TO '.$this->quoteTableName($newName));
+ }
+
+ /**
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $default
+ */
+ public function changeColumnDefault($tableName, $columnName, $default)
+ {
+ $this->_clearTableCache($tableName);
+
+ $quotedTableName = $this->quoteTableName($tableName);
+ $quotedColumnName = $this->quoteColumnName($columnName);
+
+ $sql = "SHOW COLUMNS FROM $quotedTableName LIKE ".$this->quoteString($columnName);
+ $res = $this->selectOne($sql);
+ $currentType = $res['Type'];
+
+ $default = $this->quote($default);
+ $sql = "ALTER TABLE $quotedTableName CHANGE $quotedColumnName $quotedColumnName
+ $currentType DEFAULT $default";
+ return $this->execute($sql);
+ }
+
+ /**
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $type
+ * @param array $options
+ */
+ public function changeColumn($tableName, $columnName, $type, $options=array())
+ {
+ $this->_clearTableCache($tableName);
+
+ $quotedTableName = $this->quoteTableName($tableName);
+ $quotedColumnName = $this->quoteColumnName($columnName);
+
+ if (!array_key_exists('default', $options)) {
+ $row = $this->selectOne("SHOW COLUMNS FROM $quotedTableName LIKE ".$this->quoteString($columnName));
+ $options['default'] = $row['Default'];
+ }
+
+ $limit = !empty($options['limit']) ? $options['limit'] : null;
+ $precision = !empty($options['precision']) ? $options['precision'] : null;
+ $scale = !empty($options['scale']) ? $options['scale'] : null;
+
+ $typeSql = $this->typeToSql($type, $limit, $precision, $scale);
+
+ $sql = "ALTER TABLE $quotedTableName CHANGE $quotedColumnName $quotedColumnName $typeSql";
+ $sql = $this->addColumnOptions($sql, $options);
+ $this->execute($sql);
+ }
+
+ /**
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $newColumnName
+ */
+ public function renameColumn($tableName, $columnName, $newColumnName)
+ {
+ $this->_clearTableCache($tableName);
+
+ $quotedTableName = $this->quoteTableName($tableName);
+ $quotedColumnName = $this->quoteColumnName($columnName);
+
+ $sql = "SHOW COLUMNS FROM $quotedTableName LIKE ".$this->quoteString($columnName);
+ $res = $this->selectOne($sql);
+ $currentType = $res["Type"];
+
+ $sql = "ALTER TABLE $quotedTableName CHANGE ".
+ $quotedColumnName.' '.
+ $this->quoteColumnName($newColumnName)." ".
+ $currentType;
+ return $this->execute($sql);
+ }
+
+ /**
+ * SHOW VARIABLES LIKE 'name'
+ *
+ * @param string $name
+ * @return string
+ */
+ public function showVariable($name)
+ {
+ return $this->selectValue('SHOW VARIABLES LIKE '.$this->quoteString($name));
+ }
+
+ /**
+ * Add AFTER option
+ *
+ * @param string $sql
+ * @param array $options
+ * @return string
+ */
+ public function addColumnOptions($sql, $options)
+ {
+ $sql = parent::addColumnOptions($sql, $options);
+ if (isset($options['after'])) {
+ $sql .= " AFTER ".$this->quoteColumnName($options['after']);
+ }
+ return $sql;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Mysql_TableDefinition extends Horde_Db_Adapter_Abstract_TableDefinition
+{
+ /**
+ * @param string $name
+ * @param array $options
+ */
+ public function end()
+ {
+ if (empty($this->_options['temporary'])) {
+ $this->_options['options'] = 'ENGINE=InnoDB';
+ }
+ return parent::end();
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2006-2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * MySQL Improved Horde_Db_Adapter
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Mysqli extends Horde_Db_Adapter_Abstract
+{
+ /**
+ * Mysqli database connection object.
+ * @var mysqli
+ */
+ protected $_connection = null;
+
+ /**
+ * Last auto-generated insert_id
+ * @var integer
+ */
+ protected $_insertId;
+
+ /**
+ * @var string
+ */
+ protected $_schemaClass = 'Horde_Db_Adapter_Mysql_Schema';
+
+
+ /*##########################################################################
+ # 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:
+ * <code>
+ * $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);
+ * </code>
+ */
+ public function connect()
+ {
+ $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 {
+ $oldErrorReporting = error_reporting(0);
+ $mysqli = new mysqli(
+ $config['host'], $config['username'], $config['password'],
+ $config['dbname'], $config['port'], $config['socket']);
+ error_reporting($oldErrorReporting);
+ }
+ 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;
+ }
+
+ /**
+ * Disconnect from db
+ */
+ public function disconnect()
+ {
+ if ($this->_connection)
+ $this->_connection->close();
+ $this->_connection = null;
+ $this->_active = false;
+ }
+
+ /**
+ * Check if the connection is active
+ *
+ * @return boolean
+ */
+ public function isActive()
+ {
+ return isset($this->_connection) && $this->_connection->query('SELECT 1');
+ }
+
+
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ /**
+ * Quotes a string, escaping any ' (single quote) and \ (backslash)
+ * characters..
+ *
+ * @param string $string
+ * @return string
+ */
+ public function quoteString($string)
+ {
+ return "'".$this->_connection->real_escape_string($string)."'";
+ }
+
+
+ /*##########################################################################
+ # Database Statements
+ ##########################################################################*/
+
+ /**
+ * Returns an array of records with the column names as keys, and
+ * column values as values.
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ * @return array
+ */
+ public function select($sql, $arg1=null, $arg2=null)
+ {
+ return new Horde_Db_Adapter_Mysqli_Result($this, $sql, $arg1, $arg2);
+ }
+
+ /**
+ * Returns an array of record hashes with the column names as keys and
+ * column values as values.
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ */
+ public function selectAll($sql, $arg1=null, $arg2=null)
+ {
+ $result = $this->execute($sql, $arg1, $arg2);
+ $rows = array();
+ if ($result) {
+ while ($row = $result->fetch_array()) {
+ $rows[] = $row;
+ }
+ }
+ return $rows;
+ }
+
+ /**
+ * Returns a record hash with the column names as keys and column values
+ * as values.
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ * @return array
+ */
+ public function selectOne($sql, $arg1=null, $arg2=null)
+ {
+ $result = $this->execute($sql, $arg1, $arg2);
+ return $result ? $result->fetch_array() : 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 = $result->fetch_row()) {
+ $values[] = $row[0];
+ }
+ }
+ return $values;
+ }
+
+ /**
+ * Executes the SQL statement in the context of this connection.
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ */
+ public function execute($sql, $arg1=null, $arg2=null)
+ {
+ if (is_array($arg1)) {
+ $sql = $this->_replaceParameters($sql, $arg1);
+ $name = $arg2;
+ } else {
+ $name = $arg1;
+ }
+
+ $t = new Horde_Support_Timer;
+ $t->push();
+
+ $stmt = $this->_connection->query($sql);
+ if (!$stmt) {
+ $this->_logInfo($sql, 'QUERY FAILED: ' . $this->_connection->error);
+ $this->_logInfo($sql, $name);
+ throw new Horde_Db_Exception('QUERY FAILED: ' . $this->_connection->error . "\n\n" . $sql,
+ $this->_errorCode($this->_connection->sqlstate, $this->_connection->errno));
+ }
+
+ $this->_logInfo($sql, $name, $t->pop());
+ //@TODO if ($this->_connection->info) $this->_loginfo($sql, $this->_connection->info);
+ //@TODO also log warnings? http://php.net/mysqli.warning-count and http://php.net/mysqli.get-warnings
+
+ $this->_rowCount = $this->_connection->affected_rows;
+ $this->_insertId = $this->_connection->insert_id;
+ return $stmt;
+ }
+
+ /**
+ * Returns the last auto-generated ID from the affected table.
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ * @param string $pk
+ * @param int $idValue
+ * @param string $sequenceName
+ */
+ public function insert($sql, $arg1=null, $arg2=null, $pk=null, $idValue=null, $sequenceName=null)
+ {
+ $this->execute($sql, $arg1, $arg2);
+ return isset($idValue) ? $idValue : $this->_insertId;
+ }
+
+ /**
+ * Begins the transaction (and turns off auto-committing).
+ */
+ public function beginDbTransaction()
+ {
+ $this->_transactionStarted = true;
+ $this->_connection->autocommit(false);
+ }
+
+ /**
+ * Appends +LIMIT+ and +OFFSET+ options to a SQL statement.
+ *
+ * @param string $sql
+ * @param array $options
+ * @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;
+ }
+
+
+ /*##########################################################################
+ # 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 PDO constructor.
+ *
+ * @throws Horde_Db_Exception
+ * @return array [host, username, password, dbname, port, socket]
+ */
+ protected function _parseConfig()
+ {
+ // check required config keys are present
+ $required = array('username');
+ $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);
+ }
+
+ if (isset($this->_config['port'])) {
+ if (empty($this->_config['host'])) {
+ $msg = 'host is required if port is specified';
+ throw new Horde_Db_Exception($msg);
+ }
+ }
+
+ $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;
+ }
+
+}
--- /dev/null
+/Result.php/1.1/Fri Sep 19 03:59:03 2008//
+D
--- /dev/null
+framework/Db/lib/Horde/Db/Adapter/Mysqli
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2006-2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Mysqli_Result implements Iterator
+{
+ /**
+ * @var Horde_Db_Adapter
+ */
+ protected $_adapter;
+
+ /**
+ * @var string
+ */
+ protected $_sql;
+
+ /**
+ * @var mixed
+ */
+ protected $_arg1;
+
+ /**
+ * @var string
+ */
+ protected $arg2;
+
+ /**
+ * Result resource
+ * @var mysqli_result
+ */
+ protected $_result;
+
+ /**
+ * Current row
+ * @var array
+ */
+ protected $_current;
+
+ /**
+ * Current offset
+ * @var integer
+ */
+ protected $_index;
+
+ /**
+ * Are we at the end of the result?
+ * @var boolean
+ */
+ protected $_eof;
+
+ /**
+ * Constructor
+ *
+ * @param Horde_Db_Adapter $adapter
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ */
+ public function __construct($adapter, $sql, $arg1 = null, $arg2 = null)
+ {
+ $this->_adapter = $adapter;
+ $this->_sql = $sql;
+ $this->_arg1 = $arg1;
+ $this->_arg2 = $arg2;
+ }
+
+ /**
+ * Destructor - release any resources.
+ */
+ public function __destruct()
+ {
+ if ($this->_result) {
+ unset($this->_result);
+ }
+ }
+
+ /**
+ * Implementation of the rewind() method for iterator.
+ */
+ public function rewind()
+ {
+ if ($this->_result) {
+ unset($this->_result);
+ }
+ $this->_current = null;
+ $this->_index = null;
+ $this->_eof = true;
+ $this->_result = $this->_adapter->execute($this->_sql, $this->_arg1, $this->_arg2);
+
+ $this->next();
+ }
+
+ /**
+ * Implementation of the current() method for iterator.
+ *
+ * @return mixed The current row, or null if no rows.
+ */
+ public function current()
+ {
+ if (is_null($this->_result)) {
+ $this->rewind();
+ }
+ return $this->_current;
+ }
+
+ /**
+ * Implementation of the key() method for iterator.
+ *
+ * @return mixed The current row number (starts at 0), or NULL if no rows
+ */
+ public function key()
+ {
+ if (is_null($this->_result)) {
+ $this->rewind();
+ }
+ return $this->_index;
+ }
+
+ /**
+ * Implementation of the next() method.
+ *
+ * @return array|null The next row in the resultset or null if there are no
+ * more results.
+ */
+ public function next()
+ {
+ if (is_null($this->_result)) {
+ $this->rewind();
+ }
+
+ if ($this->_result) {
+ $row = $this->_result->fetch_array(MYSQLI_BOTH);
+ 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;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/*
+I have been exploring ZF to implement for my client and found a few
+interesting things that I thought might be of interest to others using
+the Oracle oci8 driver. While not really "broken", the support for the
+Oracle oci8 driver does appear to be missing a few useful / convenient
+features:
+
+The Zend_Db_Oracle driver ignores the 'host' parameter. This is only
+an issue when PHP is compiled with Oracles instant client which need
+not use TNS. For instance the following $dsn throws the following
+exception "TNS:could not resolve the connect identifier specified"
+
+Fails:
+
+$dsn = array(
+ 'host' =>'myhost',
+ 'username' => 'zend',
+ 'password' => 'zend',
+ 'dbname' => 'xe',
+ 'options' => $options
+);
+
+This is not surprising since the instant client does not use TNS as
+stated. To get this to work you need to prepend the 'dbname' parameter
+with the host name, which although easy to do is not very intuitive.
+
+Works:
+
+$dsn = array(
+ 'host' => 'totally ignored',
+ 'username' => 'zend',
+ 'password' => 'zend',
+ 'dbname' => 'myhost/xe',
+ 'options' => $options
+);
+
+Zend_Db_Statement_Oracle
+
+This class states that it does not support case folding due to a
+limitation of the Oci8 driver. The driver may not support case
+folding, but after a quick review of the code I was curious as to why
+this was not emulated, at least for results being returned as an
+associative array (objects would not be much of a stretch either)?
+This would be a great feature to have and save those looking for this
+feature from having to call the array_change_key_case() function on
+every row returned J
+*/
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Oci8 extends Horde_Db_Adapter_Abstract
+{
+ /**
+ * @var string
+ */
+ protected $_schemaClass = 'Horde_Db_Adapter_Oracle_Schema';
+
+ /**
+ * Build a connection string and connect to the database server.
+ *
+ * @TODO http://blogs.oracle.com/opal/discuss/msgReader$111
+ */
+ public function connect()
+ {
+ }
+
+}
--- /dev/null
+/Schema.php/1.1/Fri Sep 19 03:59:03 2008//
+D
--- /dev/null
+framework/Db/lib/Horde/Db/Adapter/Oracle
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Oracle_Schema extends Horde_Db_Adapter_Abstract_Schema
+{
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ /**
+ * @return string
+ */
+ public function quoteColumnName($name)
+ {
+ return '"' . str_replace('"', '""', $name) . '"';
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2006-2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+abstract class Horde_Db_Adapter_Pdo_Abstract extends Horde_Db_Adapter_Abstract
+{
+ /*##########################################################################
+ # Connection Management
+ ##########################################################################*/
+
+ /**
+ * Connect to the db
+ */
+ public function connect()
+ {
+ list($dsn, $user, $pass) = $this->_parseConfig();
+
+ $oldErrorReporting = error_reporting(0);
+ try {
+ $pdo = new PDO($dsn, $user, $pass);
+ error_reporting($oldErrorReporting);
+ } catch (PDOException $e) {
+ error_reporting($oldErrorReporting);
+ $msg = "Could not instantiate PDO with DSN \"$dsn\". PDOException: "
+ . $e->getMessage();
+ throw new Horde_Db_Exception($msg);
+ }
+
+ $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+ $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
+
+ $this->_connection = $pdo;
+ $this->_active = true;
+ }
+
+ /**
+ * Check if the connection is active
+ *
+ * @return boolean
+ */
+ public function isActive()
+ {
+ return isset($this->_connection) && $this->_connection->query('SELECT 1');
+ }
+
+
+ /*##########################################################################
+ # Database Statements
+ ##########################################################################*/
+
+ /**
+ * Returns an array of record hashes with the column names as keys and
+ * column values as values.
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ */
+ public function selectAll($sql, $arg1=null, $arg2=null)
+ {
+ $result = $this->execute($sql, $arg1, $arg2);
+ return $result ? $result->fetchAll(PDO::FETCH_BOTH) : array();
+ }
+
+ /**
+ * Returns a record hash with the column names as keys and column values
+ * as values.
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ * @return array
+ */
+ public function selectOne($sql, $arg1=null, $arg2=null)
+ {
+ $result = $this->execute($sql, $arg1, $arg2);
+ return $result ? $result->fetch(PDO::FETCH_BOTH) : array();
+ }
+
+ /**
+ * Returns a single value from a record
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ * @return string
+ */
+ public function selectValue($sql, $arg1=null, $arg2=null)
+ {
+ $result = $this->execute($sql, $arg1, $arg2);
+ return $result ? $result->fetchColumn(0) : null;
+ }
+
+ /**
+ * Returns an array of the values of the first column in a select:
+ * 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)
+ {
+ $result = $this->execute($sql, $arg1, $arg2);
+ return $result ? $result->fetchAll(PDO::FETCH_COLUMN, 0) : array();
+ }
+
+
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ /**
+ * Quotes a string, escaping any ' (single quote) and \ (backslash)
+ * characters..
+ *
+ * @param string $string
+ * @return string
+ */
+ public function quoteString($string)
+ {
+ return $this->_connection->quote($string);
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * 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
+ $required = array('adapter', 'username');
+ $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);
+ }
+
+ // 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']);
+
+ // rewrite rails config key names to pdo equivalents
+ $rails2pdo = array('database' => 'dbname', 'socket' => 'unix_socket');
+ foreach ($rails2pdo as $from => $to) {
+ if (isset($dsnOpts[$from])) {
+ $dsnOpts[$to] = $dsnOpts[$from];
+ unset($dsnOpts[$from]);
+ }
+ }
+
+ // build DSN string
+ $dsn = $this->_config['adapter'] . ':';
+ foreach ($dsnOpts as $k => $v) {
+ $dsn .= "$k=$v;";
+ }
+ $dsn = rtrim($dsn, ';');
+
+ // return DSN and user/pass for connection
+ return array(
+ $dsn,
+ $this->_config['username'],
+ $this->_config['password']);
+ }
+
+}
--- /dev/null
+/Abstract.php/1.1/Fri Sep 19 03:59:03 2008//
+/Mysql.php/1.1/Fri Sep 19 03:59:03 2008//
+/Oci.php/1.1/Fri Sep 19 03:59:03 2008//
+/Sqlite.php/1.1/Fri Sep 19 03:59:03 2008//
+/Pgsql.php/1.2/Wed Oct 22 01:39:49 2008//
+D
--- /dev/null
+framework/Db/lib/Horde/Db/Adapter/Pdo
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * PDO_MySQL Horde_Db_Adapter
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Pdo_Mysql extends Horde_Db_Adapter_Pdo_Abstract
+{
+ /**
+ * @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()
+ {
+ parent::connect();
+
+ // ? $this->_connection->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true);
+ // SET NAMES ?
+ }
+
+
+ /*##########################################################################
+ # 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';
+
+ if (isset($this->_config['port'])) {
+ if (empty($this->_config['host'])) {
+ $msg = 'host is required if port is specified';
+ throw new Horde_Db_Exception($msg);
+ }
+
+ if (preg_match('/[^\d\.]/', $this->_config['host'])) {
+ $msg = 'pdo_mysql ignores port unless IP address is used for host';
+ throw new Horde_Db_Exception($msg);
+ }
+ }
+
+ return parent::_parseConfig();
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2006-2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/*
+I have been exploring ZF to implement for my client and found a few
+interesting things that I thought might be of interest to others using
+the Oracle oci8 driver. While not really "broken", the support for the
+Oracle oci8 driver does appear to be missing a few useful / convenient
+features:
+
+The Zend_Db_Oracle driver ignores the 'host' parameter. This is only
+an issue when PHP is compiled with Oracles instant client which need
+not use TNS. For instance the following $dsn throws the following
+exception "TNS:could not resolve the connect identifier specified"
+
+Fails:
+
+$dsn = array(
+ 'host' =>'myhost',
+ 'username' => 'zend',
+ 'password' => 'zend',
+ 'dbname' => 'xe',
+ 'options' => $options
+);
+
+This is not surprising since the instant client does not use TNS as
+stated. To get this to work you need to prepend the 'dbname' parameter
+with the host name, which although easy to do is not very intuitive.
+
+Works:
+
+$dsn = array(
+ 'host' => 'totally ignored',
+ 'username' => 'zend',
+ 'password' => 'zend',
+ 'dbname' => 'myhost/xe',
+ 'options' => $options
+);
+
+Zend_Db_Statement_Oracle
+
+This class states that it does not support case folding due to a
+limitation of the Oci8 driver. The driver may not support case
+folding, but after a quick review of the code I was curious as to why
+this was not emulated, at least for results being returned as an
+associative array (objects would not be much of a stretch either)?
+This would be a great feature to have and save those looking for this
+feature from having to call the array_change_key_case() function on
+every row returned J
+*/
+
+/**
+ * OCI PDO Horde_Db_Adapter
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Pdo_Oci extends Horde_Db_Adapter_Pdo_Abstract
+{
+ /**
+ * @var string
+ */
+ protected $_schemaClass = 'Horde_Db_Adapter_Oracle_Schema';
+
+ /**
+ * Get the appropriate DML object and call the parent constructor.
+ *
+ * @param array $options Connection options.
+ */
+ public function __construct($options = array())
+ {
+ parent::__construct($options);
+ }
+
+ /**
+ * Get a description of the database table that $model is going to
+ * reflect.
+ */
+ public function loadModel($model)
+ {
+ $table = $this->dml->quoteColumnName(strtoupper($model->table));
+ $tblinfo = $this->select('SELECT column_name, data_type, data_length, nullable, data_default FROM all_tab_columns WHERE table_name = '
+ . $table);
+ while ($col = $tblinfo->fetch()) {
+ $model->addField($col['column_name'], array('type' => $col['data_type'],
+ 'null' => ($col['nullable'] != 'N'),
+ 'default' => $col['data_default'],
+ 'length' => $col['data_length']));
+ }
+
+ // Only fetch the first primary key for now.
+ $model->key = $this->selectOne('SELECT DISTINCT b.column_name FROM all_constraints a, all_cons_columns b WHERE a.table_name = '
+ . $table . ' AND a.constraint_type = \'P\' AND b.constraint_name = a.constraint_name');
+ }
+
+ /**
+ */
+ protected function _lastInsertId($sequence)
+ {
+ $data = $this->selectOne('SELECT ' . $this->dml->quoteColumnName($sequence) . '.currval FROM dual');
+ }
+
+ /**
+ */
+ public function getTables()
+ {
+ return 'SELECT table_name FROM all_tables';
+ }
+
+ /**
+ */
+ protected function _limit($query, &$sql, &$bindParams)
+ {
+ if ($query->limit) {
+
+ $sql = 'SELECT q2.* FROM (SELECT rownum r, q1.* FROM (' . $sql . ') q1) q2
+ WHERE r BETWEEN ' . $query->limitOffset . ' AND ' . ($query->limit + $query->limitOffset);
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * PDO_PostgreSQL Horde_Db_Adapter
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Pdo_Pgsql extends Horde_Db_Adapter_Pdo_Abstract
+{
+ /**
+ * @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.
+ $clientMinMessageOld = $this->getClientMinMessages();
+ $this->setClientMinMessages('panic');
+
+ $hasSupport = $this->selectValue('SHOW standard_conforming_strings');
+
+ $this->setClientMinMessages($clientMinMessagesOld);
+ return $hasSupport;
+ }
+
+ public function supportsInsertWithReturning()
+ {
+ return $this->postgresqlVersion() >= 80200;
+ }
+
+
+ /*##########################################################################
+ # Connection Management
+ ##########################################################################*/
+
+ /**
+ * Connect to the db
+ */
+ public function connect()
+ {
+ parent::connect();
+
+ $retval = $this->_connection->exec("SET datestyle TO 'iso'");
+ if ($retval === false) {
+ $error = $this->_connection->errorInfo();
+ throw new Horde_Db_Exception($error[2]);
+ }
+
+ // Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
+ // PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
+ // should know about this but can't detect it there, so deal with it here.
+ Horde_Db_Adapter_Postgresql_Column::$moneyPrecision = ($this->postgresqlVersion() >= 80300) ? 19 : 10;
+
+ $this->_configureConnection();
+ }
+
+
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ /**
+ * @return string
+ */
+ public function quotedStringPrefix()
+ {
+ // Use escape string syntax if available. We cannot do this lazily when encountering
+ // the first string, because that could then break any transactions in progress.
+ // See: http://www.postgresql.org/docs/current/static/runtime-config-compatible.html
+ // If PostgreSQL doesn't know the standard_conforming_strings parameter then it doesn't
+ // support escape string syntax. Don't override the inherited quotedStringPrefix.
+ if ($this->supportsStandardConformingStrings()) {
+ return 'E';
+ }
+
+ return '';
+ }
+
+
+ /*##########################################################################
+ # Database Statements
+ ##########################################################################*/
+
+ /**
+ * 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)
+ {
+ // Extract the table from the insert sql. Yuck.
+ $temp = explode(' ', $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));
+ return $id;
+ }
+ }
+
+ // Otherwise, insert then grab last_insert_id.
+ if ($insertId = parent::insert($sql, $arg1, $arg2, $pk, $idValue, $sequenceName)) {
+ return $insertId;
+ }
+
+ // If neither pk nor sequence name is given, look them up.
+ if (!($pk || $sequenceName)) {
+ list($pk, $sequenceName) = $this->pkAndSequenceFor($table);
+ }
+
+ // 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))) {
+ return $this->_lastInsertId($table, $sequenceName);
+ }
+ }
+
+
+ /*##########################################################################
+ # 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';
+
+ 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['encoding'])) {
+ $this->_connection->execute('SET client_encoding TO '.$this->quoteString($this->_config['encoding']));
+ }
+
+ 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->quoteColumnName($sequenceName).')');
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * PDO_SQLite Horde_Db_Adapter
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Pdo_Sqlite extends Horde_Db_Adapter_Pdo_Abstract
+{
+ /**
+ * @var string
+ */
+ protected $_schemaClass = 'Horde_Db_Adapter_Sqlite_Schema';
+
+ /**
+ * SQLite version number
+ * @var integer
+ */
+ protected $_sqliteVersion;
+
+ /**
+ * @return string
+ */
+ public function adapterName()
+ {
+ return 'PDO_SQLite';
+ }
+
+ /**
+ * @return boolean
+ */
+ public function supportsMigrations()
+ {
+ return true;
+ }
+
+ /**
+ * Does this adapter support using DISTINCT within COUNT? This is +true+
+ * for all adapters except sqlite.
+ *
+ * @return boolean
+ */
+ public function supportsCountDistinct()
+ {
+ return $this->_sqliteVersion >= '3.2.6';
+ }
+
+ public function supportsAutoIncrement()
+ {
+ return $this->_sqliteVersion >= '3.1.0';
+ }
+
+
+ /*##########################################################################
+ # Connection Management
+ ##########################################################################*/
+
+ /**
+ * Connect to the db
+ */
+ public function connect()
+ {
+ parent::connect();
+
+ $this->_connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
+
+ $retval = $this->_connection->exec('PRAGMA full_column_names=0');
+ if ($retval === false) {
+ $error = $this->_connection->errorInfo();
+ throw new Horde_Db_Exception($error[2]);
+ }
+
+ $retval = $this->_connection->exec('PRAGMA short_column_names=1');
+ if ($retval === false) {
+ $error = $this->_connection->errorInfo();
+ throw new Horde_Db_Exception($error[2]);
+ }
+
+ $this->_sqliteVersion = $this->selectValue('SELECT sqlite_version(*)');
+ }
+
+
+ /*##########################################################################
+ # 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, $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;
+ }
+ }
+ }
+
+ /**
+ * Parse YAML 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']);
+
+ // rewrite rails config key names to pdo equivalents
+ $rails2pdo = array('database' => 'dbname');
+ foreach ($rails2pdo as $from => $to) {
+ if (isset($dsnOpts[$from])) {
+ $dsnOpts[$to] = $dsnOpts[$from];
+ unset($dsnOpts[$from]);
+ }
+ }
+
+ // build DSN string
+ $dsn = 'sqlite:' . $dsnOpts['dbname'];
+
+ // return DSN and dummy user/pass for connection
+ return array($dsn, '', '');
+ }
+
+}
--- /dev/null
+/Column.php/1.1/Fri Sep 19 03:59:03 2008//
+/Schema.php/1.1/Fri Sep 19 03:59:03 2008//
+D
--- /dev/null
+framework/Db/lib/Horde/Db/Adapter/Postgresql
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Postgresql_Column extends Horde_Db_Adapter_Abstract_Column
+{
+ /*##########################################################################
+ # Constants
+ ##########################################################################*/
+
+ /**
+ * The internal PostgreSQL identifier of the money data type.
+ * @const integer
+ */
+ const MONEY_COLUMN_TYPE_OID = 790;
+
+
+ /**
+ * @var integer
+ */
+ public static $moneyPrecision = 19;
+
+ /**
+ * @var array
+ */
+ protected static $_hasEmptyStringDefault = array('binary', 'string', 'text');
+
+
+ /**
+ * Construct
+ * @param string $name
+ * @param string $default
+ * @param string $sqlType
+ * @param boolean $null
+ */
+ public function __construct($name, $default, $sqlType=null, $null=true)
+ {
+ parent::__construct($name, $this->_extractValueFromDefault($default), $sqlType, $null);
+ }
+
+ /**
+ * @param string $fieldType
+ * @return string
+ */
+ protected function _simplifiedType($fieldType)
+ {
+ switch (true) {
+ // Numeric and monetary types
+ case preg_match('/^(?:real|double precision)$/', $fieldType):
+ return 'float';
+ // Monetary types
+ case preg_match('/^money$/', $fieldType):
+ return 'decimal';
+ // Character types
+ case preg_match('/^(?:character varying|bpchar)(?:\(\d+\))?$/', $fieldType):
+ return 'string';
+ // Binary data types
+ case preg_match('/^bytea$/', $fieldType):
+ return 'binary';
+ // Date/time types
+ case preg_match('/^timestamp with(?:out)? time zone$/', $fieldType):
+ return 'datetime';
+ case preg_match('/^interval$/', $fieldType):
+ return 'string';
+ // Geometric types
+ case preg_match('/^(?:point|line|lseg|box|"?path"?|polygon|circle)$/', $fieldType):
+ return 'string';
+ // Network address types
+ case preg_match('/^(?:cidr|inet|macaddr)$/', $fieldType):
+ return 'string';
+ // Bit strings
+ case preg_match('/^bit(?: varying)?(?:\(\d+\))?$/', $fieldType):
+ return 'string';
+ // XML type
+ case preg_match('/^xml$/', $fieldType):
+ return 'string';
+ // Arrays
+ case preg_match('/^\D+\[\]$/', $fieldType):
+ return 'string';
+ // Object identifier types
+ case preg_match('/^oid$/', $fieldType):
+ return 'integer';
+ }
+
+ // Pass through all types that are not specific to PostgreSQL.
+ return parent::_simplifiedType($fieldType);
+ }
+
+ /**
+ * Extracts the value from a PostgreSQL column default definition.
+ */
+ protected function _extractValueFromDefault($default)
+ {
+ switch (true) {
+ // Numeric types
+ case preg_match('/\A-?\d+(\.\d*)?\z/', $default):
+ return $default;
+ // Character types
+ case preg_match('/\A\'(.*)\'::(?:character varying|bpchar|text)\z/m', $default, $matches):
+ return $matches[1];
+ // Character types (8.1 formatting)
+ case preg_match('/\AE\'(.*)\'::(?:character varying|bpchar|text)\z/m', $default, $matches):
+ /*@TODO fix preg callback*/
+ return preg_replace('/\\(\d\d\d)/', '$1.oct.chr', $matches[1]);
+ // Binary data types
+ case preg_match('/\A\'(.*)\'::bytea\z/m', $default, $matches):
+ return $matches[1];
+ // Date/time types
+ case preg_match('/\A\'(.+)\'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/', $default, $matches):
+ return $matches[1];
+ case preg_match('/\A\'(.*)\'::interval\z/', $default, $matches):
+ return $matches[1];
+ // Boolean type
+ case $default == 'true':
+ return true;
+ case $default == 'false':
+ return false;
+ // Geometric types
+ case preg_match('/\A\'(.*)\'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/', $default, $matches):
+ return $matches[1];
+ // Network address types
+ case preg_match('/\A\'(.*)\'::(?:cidr|inet|macaddr)\z/', $default, $matches):
+ return $matches[1];
+ // Bit string types
+ case preg_match('/\AB\'(.*)\'::"?bit(?: varying)?"?\z/', $default, $matches):
+ return $matches[1];
+ // XML type
+ case preg_match('/\A\'(.*)\'::xml\z/m', $default, $matches):
+ return $matches[1];
+ // Arrays
+ case preg_match('/\A\'(.*)\'::"?\D+"?\[\]\z/', $default, $matches):
+ return $matches[1];
+ // Object identifier types
+ case preg_match('/\A-?\d+\z/', $default, $matches):
+ return $matches[1];
+ default:
+ // Anything else is blank, some user type, or some function
+ // and we can't know the value of that, so return nil.
+ return null;
+ }
+ }
+
+ /**
+ * @TODO
+ * http://us3.php.net/manual/en/pdo.lobs.php
+ * http://us2.php.net/manual/en/function.pdo-pgsqllobopen.php
+ * etc.
+ */
+ /*@TODO bollux
+ // Escapes binary strings for bytea input to the database.
+ def self.string_to_binary(value)
+ if PGconn.respond_to?(:escape_bytea)
+ self.class.module_eval do
+ define_method(:string_to_binary) do |value|
+ PGconn.escape_bytea(value) if value
+ end
+ end
+ else
+ self.class.module_eval do
+ define_method(:string_to_binary) do |value|
+ if value
+ result = ''
+ value.each_byte { |c| result << sprintf('\\\\%03o', c) }
+ result
+ end
+ end
+ end
+ end
+ self.class.string_to_binary(value)
+ end
+
+ // Unescapes bytea output from a database to the binary string it represents.
+ def self.binary_to_string(value)
+ // In each case, check if the value actually is escaped PostgreSQL bytea output
+ // or an unescaped Active Record attribute that was just written.
+ if PGconn.respond_to?(:unescape_bytea)
+ self.class.module_eval do
+ define_method(:binary_to_string) do |value|
+ if value =~ /\\\d{3}/
+ PGconn.unescape_bytea(value)
+ else
+ value
+ end
+ end
+ end
+ else
+ self.class.module_eval do
+ define_method(:binary_to_string) do |value|
+ if value =~ /\\\d{3}/
+ result = ''
+ i, max = 0, value.size
+ while i < max
+ char = value[i]
+ if char == ?\\
+ if value[i+1] == ?\\
+ char = ?\\
+ i += 1
+ else
+ char = value[i+1..i+3].oct
+ i += 3
+ end
+ end
+ result << char
+ i += 1
+ end
+ result
+ else
+ value
+ end
+ end
+ end
+ end
+ self.class.binary_to_string(value)
+ end
+ */
+
+
+ /*##########################################################################
+ # 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);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Postgresql_Schema extends Horde_Db_Adapter_Abstract_Schema
+{
+ /**
+ * @var string
+ */
+ protected $_schemaSearchPath = '';
+
+
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ /**
+ * Quotes column names for use in SQL queries.
+ *
+ * @return string
+ */
+ public function quoteColumnName($name)
+ {
+ return '"' . str_replace('"', '""', $name) . '"';
+ }
+
+ /**
+ * Quotes PostgreSQL-specific data types for SQL input.
+ */
+ public function quote($value, $column = null)
+ {
+ if (!$column)
+ return parent::quote($value, $column);
+
+ if (is_string($value) && $column->getType() == 'binary' && method_exists($column, 'stringToBinary')) {
+ /*@TODO test blobs/bytea fields with postgres/pdo and figure out how
+ this should work */
+ return $this->quotedStringPrefix() . "'" . $column->stringToBinary($value) . "'";
+ } elseif (is_string($value) && $column->getSqlType() == 'xml') {
+ return "xml '" . $this->quoteString($value) . "'";
+ } elseif (is_numeric($value) && $column->getSqlType() == 'money') {
+ // Not truly string input, so doesn't require (or allow) escape string syntax.
+ return "'" . $value . "'";
+ } elseif (is_string($value) && substr($column->getSqlType(), 0, 3) == 'bit') {
+ if (preg_match('/^[01]*$/', $value)) {
+ // Bit-string notation
+ return "B'" . $value . "'";
+ } elseif (preg_match('/^[0-9A-F]*$/i')) {
+ // Hexadecimal notation
+ return "X'" . $value . "'";
+ }
+ }
+
+ return parent::quote($value, $column);
+ }
+
+
+ /*##########################################################################
+ # Schema Statements
+ ##########################################################################*/
+
+ /**
+ * The db column types for this adapter
+ *
+ * @return array
+ */
+ public function nativeDatabaseTypes()
+ {
+ return array(
+ 'primaryKey' => 'serial primary key',
+ 'string' => array('name' => 'character varying', 'limit' => 255),
+ 'text' => 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 configured supported identifier length supported by PostgreSQL,
+ * or report the default of 63 on PostgreSQL 7.x.
+ */
+ public function tableAliasLength()
+ {
+ if ($this->postgresqlVersion() >= 80000) {
+ return (int)$this->selectValue('SHOW max_identifier_length');
+ } else return 63;
+ }
+
+ /**
+ * Returns a string of <tt>CREATE TABLE</tt> SQL statement(s) for recreating the
+ * entire structure of the database.
+ *
+ * @param string $table
+ * @return string
+ */
+ public function structureDump($table=null)
+ {
+ }
+
+ /**
+ * Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
+ * <tt>:encoding</tt>, <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
+ * <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
+ *
+ * Example:
+ * create_database config[:database], config
+ * create_database 'foo_development', :encoding => 'unicode'
+ */
+ public function createDatabase($name, $options = array())
+ {
+ $options = array_merge(array('encoding' => 'utf8'), $options);
+
+ $optionString = '';
+ foreach ($options as $key => $value) {
+ switch ($key) {
+ case 'owner':
+ $optionString .= " OWNER = '$value'";
+ break;
+ case 'template':
+ $optionString .= " TEMPLATE = $value";
+ break;
+ case 'encoding':
+ $optionString .= " ENCODING = '$value'";
+ break;
+ case 'tablespace':
+ $optionString .= " TABLESPACE = $value";
+ case 'connection_limit':
+ $optionString .= " CONNECTION LIMIT = $value";
+ }
+ }
+
+ return $this->execute('CREATE DATABASE ' . $this->quoteTableName($name) . $optionString);
+ }
+
+ /**
+ * Drops a PostgreSQL database
+ *
+ * Example:
+ * dropDatabase('matt_development')
+ */
+ public function dropDatabase($name)
+ {
+ if ($this->postgresqlVersion() >= 80200) {
+ return $this->execute('DROP DATABASE IF EXISTS ' . $this->quoteTableName($name));
+ } else {
+ try {
+ return $this->execute('DROP DATABASE ' . $this->quoteTableName($name));
+ } catch (Horde_Db_Exception $e) {
+ /*@TODO logger.warn "#{name} database doesn't exist." if logger */
+ }
+ }
+ }
+
+ /**
+ * Returns the current database name.
+ */
+ public function currentDatabase()
+ {
+ return $this->selectValue('SELECT current_database()');
+ }
+
+ /**
+ * Returns the list of all tables in the schema search path or a specified schema.
+ */
+ public function tables($name = null)
+ {
+ $schemas = array();
+ foreach (explode(',', $this->getSchemaSearchPath()) as $p) {
+ $schemas[] = $this->quote($p);
+ }
+
+ return $this->selectValues('SELECT tablename FROM pg_tables WHERE schemaname IN (' . implode(',', $schemas) . ')', $name);
+ }
+
+ /**
+ * Returns the list of all indexes for a table.
+ */
+ public function indexes($tableName, $name = null)
+ {
+ $schemas = array();
+ foreach (explode(',', $this->getSchemaSearchPath()) as $p) {
+ $schemas[] = $this->quote($p);
+ }
+
+ $sql = "
+ SELECT distinct i.relname, d.indisunique, a.attname
+ FROM pg_class t, pg_class i, pg_index d, pg_attribute a
+ WHERE i.relkind = 'i'
+ AND d.indexrelid = i.oid
+ AND d.indisprimary = 'f'
+ AND t.oid = d.indrelid
+ AND t.relname = " . $this->quote($tableName) . "
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (" . implode(',', $schemas) . ") )
+ AND a.attrelid = t.oid
+ AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
+ OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
+ OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
+ OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
+ OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
+ ORDER BY i.relname";
+
+ $result = $this->select($sql, $name);
+
+ $currentIndex = null;
+ $indexes = array();
+
+ foreach ($result as $row) {
+ if ($currentIndex != $row[0]) {
+ $indexes[] = (object)array('table' => $tableName,
+ 'name' => $row[0],
+ 'unique' => $row[1] == 't',
+ 'columns' => array());
+ $currentIndex = $row[0];
+ }
+ $indexes[sizeof($indexes)-1]->columns[] = $row[2];
+ }
+
+ return $indexes;
+ }
+
+ /**
+ * Returns the list of all column definitions for a table.
+ */
+ public function columns($tableName, $name = null)
+ {
+ // check cache
+ $rows = @unserialize($this->_cache->get("tables/$tableName"));
+
+ // query to build rows
+ if (!$rows) {
+ $rows = $this->columnDefinitions($tableName, $name);
+
+ // write cache
+ $this->_cache->set("tables/$tableName", serialize($rows));
+ }
+
+ // create columns from rows
+ $columns = array();
+ foreach ($rows as $row) {
+ $columns[] = new Horde_Db_Adapter_Postgresql_Column(
+ $row[0], $row[2], $row[1], !(boolean)$row[3]);
+ }
+ return $columns;
+ }
+
+ /**
+ * Returns the current database 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
+ *
+ * This should be not be called manually but set in database.yml.
+ */
+ public function setSchemaSearchPath($schemaCsv)
+ {
+ if ($schemaCsv) {
+ $this->execute("SET search_path TO $schemaCsv");
+ $this->_schemaSearchPath = $schemaCsv;
+ }
+ }
+
+ /**
+ * Returns the active schema search path.
+ */
+ public function getSchemaSearchPath()
+ {
+ if (!$this->_schemaSearchPath) {
+ $this->_schemaSearchPath = $this->selectValue('SHOW search_path');
+ }
+ return $this->_schemaSearchPath;
+ }
+
+ /**
+ * Returns the current client message level.
+ */
+ public function getClientMinMessages()
+ {
+ return $this->selectValue('SHOW client_min_messages');
+ }
+
+ /**
+ * Set the client message level.
+ */
+ 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.
+ */
+ 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.
+ */
+ public function resetPkSequence($table, $pk = null, $sequence = null)
+ {
+ if (!($pk && $sequence)) {
+ list($defaultPk, $efaultSequence) = $this->pkAndSequenceFor($table);
+ if (!$pk) $pk = $defaultPk;
+ if (!$sequence) $sequence = $defaultSequence;
+ }
+
+ if ($pk) {
+ if ($sequence) {
+ $quotedSequence = $this->quoteColumnName($sequence);
+ $quotedTable = $this->quoteTableName($table);
+ $quotedPk = $this->quoteColumnName($pk);
+
+ $sql = "SELECT setval('$quotedSequence', (SELECT COALESCE(MAX($quotedPk)+(SELECT increment_by FROM $quotedSequence), (SELECT min_value FROM $quotedSequence)) FROM $quotedTable), false)";
+ $this->selectValue($sql, 'Reset sequence');
+ } else {
+ /*@TODO logger.warn "$table has primary key $pk with no default sequence" if logger*/
+ }
+ }
+ }
+
+ /**
+ * Returns a table's primary key and belonging sequence.
+ */
+ 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
+ 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[0], $result[1]);
+ }
+
+ /**
+ * Renames a table.
+ */
+ public function renameTable($name, $newName)
+ {
+ $this->_clearTableCache($name);
+
+ return $this->execute('ALTER TABLE ' . $this->quoteTableName($name) . ' RENAME TO ' . $this->quoteTableName($newName));
+ }
+
+ /**
+ * Adds a new column to the named table.
+ * See TableDefinition#column for details of the options you can use.
+ */
+ public function addColumn($tableName, $columnName, $type, $options = array())
+ {
+ $this->_clearTableCache($tableName);
+
+ $limit = isset($options['limit']) ? $options['limit'] : null;
+ $precision = isset($options['precision']) ? $options['precision'] : null;
+ $scale = isset($options['scale']) ? $options['scale'] : null;
+
+ // Add the column.
+ $this->execute('ALTER TABLE '.$this->quoteTableName($tableName).' ADD COLUMN '.$this->quoteColumnName($columnName).' '.$this->typeToSql($type, $limit, $precision, $scale));
+
+ $default = isset($options['default']) ? $options['default'] : null;
+ $notnull = isset($options['null']) && $options['null'] === false;
+ if (array_key_exists('default', $options))
+ $this->changeColumnDefault($tableName, $columnName, $fault);
+ if ($notnull)
+ $this->changeColumnNull($tableName, $columnName, false, $default);
+ }
+
+ /**
+ * Changes the column of a table.
+ */
+ public function changeColumn($tableName, $columnName, $type, $options = array())
+ {
+ $this->_clearTableCache($tableName);
+
+ $limit = isset($options['limit']) ? $options['limit'] : null;
+ $precision = isset($options['precision']) ? $options['precision'] : null;
+ $scale = isset($options['scale']) ? $options['scale'] : null;
+
+ $quotedTableName = $this->quoteTableName($tableName);
+
+ try {
+ $this->execute('ALTER TABLE '.$quotedTableName.' ALTER COLUMN '.$this->quoteColumnName($columnName).' TYPE '.$this->typeToSql($type, $limit, $precision, $scale));
+ } catch (Horde_Db_Exception $e) {
+ // This is PostgreSQL 7.x, or the old type could not be coerced to
+ // the new type, so we have to use a more arcane way of doing it.
+ try {
+ // Booleans can't always be cast to other data types; do extra
+ // work to handle them.
+ $oldType = null;
+ $columns = $this->columns($tableName);
+ foreach ($this->columns($tableName) as $column) {
+ if ($column->getName() == $columnName) {
+ $oldType = $column->getType();
+ break;
+ }
+ }
+ if ($oldType === null)
+ throw new Horde_Db_Exception("$tableName does not have a column '$columnName'");
+
+ $this->beginDbTransaction();
+
+ $tmpColumnName = $columnName.'_change_tmp';
+ $this->addColumn($tableName, $tmpColumnName, $type, $options);
+
+ if ($oldType == 'boolean') {
+ $this->execute('UPDATE '.$quotedTableName.' SET '.$this->quoteColumnName($tmpColumnName).' = CAST(CASE WHEN '.$this->quoteColumnName($columnName).' IS TRUE THEN 1 ELSE 0 END AS '.$this->typeToSql($type, $limit, $precision, $scale).')');
+ } else {
+ $this->execute('UPDATE '.$quotedTableName.' SET '.$this->quoteColumnName($tmpColumnName).' = CAST('.$this->quoteColumnName($columnName).' AS '.$this->typeToSql($type, $limit, $precision, $scale).')');
+ }
+
+ $this->removeColumn($tableName, $columnName);
+ $this->renameColumn($tableName, $tmpColumnName, $columnName);
+
+ $this->commitDbTransaction();
+ } catch (Horde_Db_Exception $e) {
+ $this->rollbackDbTransaction();
+ throw $e;
+ }
+ }
+
+ $default = isset($options['default']) ? $options['default'] : null;
+ if (array_key_exists('default', $options))
+ $this->changeColumnDefault($tableName, $columnName, $default);
+ if (array_key_exists('null', $options))
+ $this->changeColumnNull($tableName, $columnName, $options['null'], $default);
+ }
+
+ /**
+ * Changes the default value of a table column.
+ */
+ public function changeColumnDefault($tableName, $columnName, $default)
+ {
+ $this->_clearTableCache($tableName);
+ return $this->execute('ALTER TABLE '.$this->quoteTableName($tableName). ' ALTER COLUMN '.$this->quoteColumnName($columnName).' SET DEFAULT '.$this->quote($default));
+ }
+
+ public function changeColumnNull($tableName, $columnName, $null, $default = null)
+ {
+ $this->_clearTableCache($tableName);
+ if (!($null || is_null($default))) {
+ $this->execute('UPDATE '.$this->quoteTableName($tableName).' SET '.$this->quoteColumnName($columName).' = '.$this->quote($default).' WHERE '.$this->quoteColumnName($columnName).' IS NULL');
+ }
+ return $this->execute('ALTER TABLE '.$this->quoteTableName($tableName).' ALTER '.$this->quoteColumnName($columnName).' '.($null ? 'DROP' : 'SET').' NOT NULL');
+ }
+
+ /**
+ * Renames a column in a table.
+ */
+ public function renameColumn($tableName, $columnName, $newColumnName)
+ {
+ $this->_clearTableCache($tableName);
+ return $this->execute('ALTER TABLE '.$this->quoteTableName($tableName).' RENAME COLUMN '.$this->quoteColumnName($columnName).' TO '.$this->quoteColumnName($newColumnName));
+ }
+
+ /**
+ * Drops an index from a table.
+ */
+ public function removeIndex($tableName, $options = array())
+ {
+ $this->_clearTableCache($tableName);
+ return $this->execute('DROP INDEX '.$this->indexName($tableName, $options));
+ }
+
+ /**
+ * Maps logical Rails types to PostgreSQL-specific data types.
+ */
+ public function typeToSql($type, $limit = null, $precision = null, $scale = 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';
+ default:
+ throw new Horde_Db_Exception("No integer type has byte size $limit. Use a numeric with precision 0 instead.");
+ }
+ }
+
+ /**
+ * Returns a SELECT DISTINCT clause for a given set of columns and a given ORDER BY clause.
+ *
+ * PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
+ * requires that the ORDER BY include the distinct column.
+ *
+ * distinct("posts.id", "posts.created_at desc")
+ */
+ 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 'DISTINCT ON ('.$colummns.') '.$columns.', '.implode(', ', $orderColumns);
+ }
+
+ /**
+ * Returns an ORDER BY clause for the passed order option.
+ *
+ * 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.
+ */
+ 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 "SELECT * FROM ($sql) AS id_list ORDER BY $order";
+ }
+
+ /**
+ * 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
+ */
+ public function columnDefinitions($tableName, $name = null)
+ {
+ 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);
+ }
+
+ /**
+ * Returns the version of the connected PostgreSQL version.
+ */
+ public function postgresqlVersion()
+ {
+ try {
+ $version = $this->selectValue('SELECT version()');
+ if (preg_match('/PostgreSQL (\d+)\.(\d+)\.(\d+)/', $version, $matches))
+ return ($matches[1] * 10000) . ($matches[2] * 100) . $matches[3];
+ } catch (Exception $e) {}
+
+ return 0;
+ }
+
+}
--- /dev/null
+/Schema.php/1.1/Fri Sep 19 03:59:03 2008//
+/Column.php/1.2/Fri Sep 19 19:34:40 2008//
+D
--- /dev/null
+framework/Db/lib/Horde/Db/Adapter/Sqlite
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Sqlite_Column extends Horde_Db_Adapter_Abstract_Column
+{
+ /**
+ * @var array
+ */
+ protected static $_hasEmptyStringDefault = array('binary', 'string', 'text');
+
+
+ /*##########################################################################
+ # Type Juggling
+ ##########################################################################*/
+
+ public function stringToBinary($value)
+ {
+ return str_replace(array("\0", '%'), array('%00', '%25'), $value);
+ }
+
+ public function binaryToString($value)
+ {
+ return str_replace(array('%00', '%25'), array("\0", '%'), $value);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage Adapter
+ */
+class Horde_Db_Adapter_Sqlite_Schema extends Horde_Db_Adapter_Abstract_Schema
+{
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ /**
+ * @return string
+ */
+ public function quoteColumnName($name)
+ {
+ return '"' . str_replace('"', '""', $name) . '"';
+ }
+
+
+ /*##########################################################################
+ # Schema Statements
+ ##########################################################################*/
+
+ /**
+ * The db column types for this adapter
+ *
+ * @return array
+ */
+ public function nativeDatabaseTypes()
+ {
+ return array(
+ 'primaryKey' => $this->_defaultPrimaryKeyType(),
+ 'string' => array('name' => 'varchar', 'limit' => 255),
+ 'text' => 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),
+ );
+ }
+
+ /**
+ * Dump entire schema structure or specific table
+ *
+ * @param string $table
+ * @return string
+ */
+ public function structureDump($table=null)
+ {
+ if ($table) {
+ return $this->selectValue('SELECT sql FROM (
+ SELECT * FROM sqlite_master UNION ALL
+ SELECT * FROM sqlite_temp_master) WHERE type != \'meta\' AND name = ' . $this->quote($table));
+ } else {
+ $dump = $this->selectValues('SELECT sql FROM (
+ SELECT * FROM sqlite_master UNION ALL
+ SELECT * FROM sqlite_temp_master) WHERE type != \'meta\' AND name != \'sqlite_sequence\'');
+ return implode("\n\n", $dump);
+ }
+ }
+
+ /**
+ * Create the given db
+ *
+ * @param string $name
+ */
+ public function createDatabase($name)
+ {
+ return new PDO('sqlite:' . $name);
+ }
+
+ /**
+ * Drop the given db
+ *
+ * @param string $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');
+ }
+ }
+
+ /**
+ * Get the name of the current db
+ *
+ * @return string
+ */
+ public function currentDatabase()
+ {
+ return $this->_config['dbname'];
+ }
+
+ /**
+ * List of tables for the db
+ *
+ * @param string $name
+ */
+ public function tables($name=null)
+ {
+ 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");
+ }
+
+ /**
+ * List of indexes for the given table
+ *
+ * @param string $tableName
+ * @param string $name
+ */
+ public function indexes($tableName, $name=null)
+ {
+ $indexes = array();
+ foreach ($this->select('PRAGMA index_list(' . $this->quoteTableName($tableName) . ')') as $row) {
+ $index = (object)array('table' => $tableName,
+ 'name' => $row[1],
+ 'unique' => (bool)$row[2],
+ 'columns' => array());
+ foreach ($this->select('PRAGMA index_info(' . $this->quoteColumnName($index->name) . ')') as $field) {
+ $index->columns[] = $field[2];
+ }
+
+ $indexes[] = $index;
+ }
+ return $indexes;
+ }
+
+ /**
+ * @param string $tableName
+ * @param string $name
+ */
+ public function columns($tableName, $name=null)
+ {
+ // check cache
+ $rows = @unserialize($this->_cache->get("tables/$tableName"));
+
+ // query to build rows
+ if (!$rows) {
+ $rows = $this->selectAll('PRAGMA table_info(' . $this->quoteTableName($tableName) . ')', $name);
+
+ // write cache
+ $this->_cache->set("tables/$tableName", serialize($rows));
+ }
+
+ // create columns from rows
+ $columns = array();
+ foreach ($rows as $row) {
+ $columns[] = new Horde_Db_Adapter_Sqlite_Column(
+ $row[1], $row[4], $row[2], !(bool)$row[3]);
+ }
+ return $columns;
+ }
+
+ /**
+ * Override createTable to return a Sqlite Table Definition
+ * param string $name
+ * param array $options
+ */
+ public function createTable($name, $options=array())
+ {
+ $pk = isset($options['primaryKey']) && $options['primaryKey'] === false ? false : 'id';
+ $tableDefinition =
+ new Horde_Db_Adapter_Abstract_TableDefinition($name, $this, $options);
+ if ($pk != false) {
+ $tableDefinition->primaryKey($pk);
+ }
+ return $tableDefinition;
+ }
+
+ /**
+ * @param string $name
+ * @param string $newName
+ */
+ public function renameTable($name, $newName)
+ {
+ $this->_clearTableCache($name);
+
+ return $this->execute('ALTER TABLE ' . $this->quoteTableName($name) . ' RENAME TO ' . $this->quoteTableName($newName));
+ }
+
+ /**
+ * Adds a new column to the named table.
+ * See TableDefinition#column for details of the options you can use.
+ *
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $type
+ * @param array $options
+ */
+ 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');
+ }
+
+ parent::addColumn($tableName, $columnName, $type, $options);
+
+ // See last paragraph on http://www.sqlite.org/lang_altertable.html
+ $this->execute('VACUUM');
+ }
+
+ /**
+ * Removes the column from the table definition.
+ * ===== Examples
+ * remove_column(:suppliers, :qualification)
+ *
+ * @param string $tableName
+ * @param string $columnName
+ */
+ public function removeColumn($tableName, $columnName)
+ {
+ throw new Horde_Db_Exception('Sqlite#removeColumn is not supported');
+ /*@TODO
+ column_names.flatten.each do |column_name|
+ alter_table(table_name) do |definition|
+ definition.columns.delete(definition[column_name])
+ end
+ end
+ */
+ }
+
+ /**
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $type
+ * @param array $options
+ */
+ public function changeColumn($tableName, $columnName, $type, $options=array())
+ {
+ throw new Horde_Db_Exception('Not supported');
+ /*@TODO
+ alter_table(table_name) do |definition|
+ include_default = options_include_default?(options)
+ definition[column_name].instance_eval do
+ self.type = type
+ self.limit = options[:limit] if options.include?(:limit)
+ self.default = options[:default] if include_default
+ self.null = options[:null] if options.include?(:null)
+ end
+ end
+ */
+ }
+
+ /**
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $default
+ */
+ public function changeColumnDefault($tableName, $columnName, $default)
+ {
+ throw new Horde_Db_Exception('Not supported');
+ /*@TODO
+ alter_table(table_name) do |definition|
+ definition[column_name].default = default
+ end
+ */
+ }
+
+ /**
+ * @param string $tableName
+ * @param string $columnName
+ * @param string $newColumnName
+ */
+ public function renameColumn($tableName, $columnName, $newColumnName)
+ {
+ throw new Horde_Db_Exception('Not supported');
+ /*@TODO
+ alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
+ */
+ }
+
+ /**
+ * Remove the given index from the table.
+ *
+ * Remove the suppliers_name_index in the suppliers table (legacy support, use the second or third forms).
+ * remove_index :suppliers, :name
+ * Remove the index named accounts_branch_id in the accounts table.
+ * remove_index :accounts, :column => :branch_id
+ * Remove the index named by_branch_party in the accounts table.
+ * remove_index :accounts, :name => :by_branch_party
+ *
+ * You can remove an index on multiple columns by specifying the first column.
+ * add_index :accounts, [:username, :password]
+ * remove_index :accounts, :username
+ *
+ * @param string $tableName
+ * @param array $options
+ */
+ public function removeIndex($tableName, $options=array())
+ {
+ $this->_clearTableCache($tableName);
+
+ $index = $this->indexName($tableName, $options);
+ $sql = 'DROP INDEX '.$this->quoteColumnName($index);
+ return $this->execute($sql);
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ protected function _defaultPrimaryKeyType()
+ {
+ if ($this->supportsAutoIncrement())
+ return 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL';
+ else
+ return 'INTEGER PRIMARY KEY NOT NULL';
+ }
+
+ /*@TODO
+ def alter_table(table_name, options = {}) #:nodoc:
+ altered_table_name = "altered_#{table_name}"
+ caller = lambda {|definition| yield definition if block_given?}
+
+ transaction do
+ move_table(table_name, altered_table_name,
+ options.merge(:temporary => true))
+ move_table(altered_table_name, table_name, &caller)
+ end
+ end
+
+ def move_table(from, to, options = {}, &block) #:nodoc:
+ copy_table(from, to, options, &block)
+ drop_table(from)
+ end
+
+ def copy_table(from, to, options = {}) #:nodoc:
+ options = options.merge(:id => !columns(from).detect{|c| c.name == 'id'}.nil?)
+ create_table(to, options) do |definition|
+ @definition = definition
+ columns(from).each do |column|
+ column_name = options[:rename] ?
+ (options[:rename][column.name] ||
+ options[:rename][column.name.to_sym] ||
+ column.name) : column.name
+
+ @definition.column(column_name, column.type,
+ :limit => column.limit, :default => column.default,
+ :null => column.null)
+ end
+ @definition.primary_key(primary_key(from)) if primary_key(from)
+ yield @definition if block_given?
+ end
+
+ copy_table_indexes(from, to, options[:rename] || {})
+ copy_table_contents(from, to,
+ @definition.columns.map {|column| column.name},
+ options[:rename] || {})
+ end
+
+ def copy_table_indexes(from, to, rename = {}) #:nodoc:
+ indexes(from).each do |index|
+ name = index.name
+ if to == "altered_#{from}"
+ name = "temp_#{name}"
+ elsif from == "altered_#{to}"
+ name = name[5..-1]
+ end
+
+ to_column_names = columns(to).map(&:name)
+ columns = index.columns.map {|c| rename[c] || c }.select do |column|
+ to_column_names.include?(column)
+ end
+
+ unless columns.empty?
+ # index name can't be the same
+ opts = { :name => name.gsub(/_(#{from})_/, "_#{to}_") }
+ opts[:unique] = true if index.unique
+ add_index(to, columns, opts)
+ end
+ end
+ end
+
+ def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
+ column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
+ rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
+ from_columns = columns(from).collect {|col| col.name}
+ columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
+ quoted_columns = columns.map { |col| quote_column_name(col) } * ','
+
+ quoted_to = quote_table_name(to)
+ @connection.execute "SELECT * FROM #{quote_table_name(from)}" do |row|
+ sql = "INSERT INTO #{quoted_to} (#{quoted_columns}) VALUES ("
+ sql << columns.map {|col| quote row[column_mappings[col]]} * ', '
+ sql << ')'
+ @connection.execute sql
+ end
+ end
+ */
+
+}
--- /dev/null
+D/Adapter////
+/Adapter.php/1.1/Fri Sep 19 03:59:03 2008//
+/Exception.php/1.1/Fri Sep 19 03:59:03 2008//
--- /dev/null
+framework/Db/lib/Horde/Db
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2006-2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ */
+
+/**
+ * Db Exception class.
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ */
+class Horde_Db_Exception extends Exception
+{
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Db</name>
+ <channel>pear.horde.org</channel>
+ <summary>Horde Database Libraries</summary>
+ <description>Horde Database/SQL abstraction layer
+ </description>
+ <lead>
+ <name>Mike Naberezny</name>
+ <user>mnaberez</user>
+ <email>mike@naberezny.com</email>
+ <active>yes</active>
+ </lead>
+ <lead>
+ <name>Chuck Hagenbuch</name>
+ <user>chuck</user>
+ <email>chuck@horde.org</email>
+ <active>yes</active>
+ </lead>
+ <date>2008-09-19</date>
+ <version>
+ <release>0.1.0</release>
+ <api>0.1.0</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license uri="http://opensource.org/licenses/bsd-license.php">BSD</license>
+ <notes>* Initial release
+ </notes>
+ <contents>
+ <dir name="/">
+ <dir name="lib">
+ <dir name="Horde">
+ <dir name="Db">
+ <dir name="Adapter">
+ <dir name="Abstract">
+ <file name="Column.php" role="php" />
+ <file name="ColumnDefinition.php" role="php" />
+ <file name="Schema.php" role="php" />
+ <file name="TableDefinition.php" role="php" />
+ </dir> <!-- /lib/Horde/Db/Adapter/Abstract -->
+ <dir name="Mssql">
+ <file name="Schema.php" role="php" />
+ </dir> <!-- /lib/Horde/Db/Adapter/Mssql -->
+ <dir name="Mysql">
+ <file name="Column.php" role="php" />
+ <file name="Schema.php" role="php" />
+ <file name="TableDefinition.php" role="php" />
+ </dir> <!-- /lib/Horde/Db/Adapter/Mysql -->
+ <dir name="Mysqli">
+ <file name="Result.php" role="php" />
+ </dir> <!-- /lib/Horde/Db/Adapter/Mysqli -->
+ <dir name="Pdo">
+ <file name="Abstract.php" role="php" />
+ <file name="Mysql.php" role="php" />
+ <file name="Oci.php" role="php" />
+ <file name="Pgsql.php" role="php" />
+ <file name="Sqlite.php" role="php" />
+ </dir> <!-- /lib/Horde/Db/Adapter/Pdo -->
+ <dir name="Oracle">
+ <file name="Schema.php" role="php" />
+ </dir> <!-- /lib/Horde/Db/Adapter/Oracle -->
+ <dir name="Postgresql">
+ <file name="Column.php" role="php" />
+ <file name="Schema.php" role="php" />
+ </dir> <!-- /lib/Horde/Db/Adapter/Postgresql -->
+ <dir name="Sqlite">
+ <file name="Column.php" role="php" />
+ <file name="Schema.php" role="php" />
+ </dir> <!-- /lib/Horde/Db/Adapter/Sqlite -->
+ <file name="Abstract.php" role="php" />
+ <file name="Oci8.php" role="php" />
+ <file name="Mysqli.php" role="php" />
+ </dir> <!-- /lib/Horde/Db/Adapter -->
+ <file name="Adapter.php" role="php" />
+ <file name="Exception.php" role="php" />
+ </dir> <!-- /lib/Horde/Db -->
+ <file name="Db.php" role="php" />
+ </dir> <!-- /lib/Horde -->
+ </dir> <!-- /lib -->
+ </dir> <!-- / -->
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.2.0</min>
+ </php>
+ <pearinstaller>
+ <min>1.5.0</min>
+ </pearinstaller>
+ </required>
+ </dependencies>
+ <phprelease>
+ <filelist>
+ <install name="lib/Horde/Db/Adapter/Abstract/Column.php" as="Horde/Db/Adapter/Abstract/Column.php" />
+ <install name="lib/Horde/Db/Adapter/Abstract/ColumnDefinition.php" as="Horde/Db/Adapter/Abstract/ColumnDefinition.php" />
+ <install name="lib/Horde/Db/Adapter/Abstract/Schema.php" as="Horde/Db/Adapter/Abstract/Schema.php" />
+ <install name="lib/Horde/Db/Adapter/Abstract/TableDefinition.php" as="Horde/Db/Adapter/Abstract/TableDefinition.php" />
+ <install name="lib/Horde/Db/Adapter/Abstract.php" as="Horde/Db/Adapter/Abstract.php" />
+
+ <install name="lib/Horde/Db/Adapter/Mssql/Schema.php" as="Horde/Db/Adapter/Mssql/Schema.php" />
+
+ <install name="lib/Horde/Db/Adapter/Mysql/Column.php" as="Horde/Db/Adapter/Mysql/Column.php" />
+ <install name="lib/Horde/Db/Adapter/Mysql/Schema.php" as="Horde/Db/Adapter/Mysql/Schema.php" />
+ <install name="lib/Horde/Db/Adapter/Mysql/TableDefinition.php" as="Horde/Db/Adapter/Mysql/TableDefinition.php" />
+
+ <install name="lib/Horde/Db/Adapter/Mysqli/Result.php" as="Horde/Db/Adapter/Mysqli/Statement.php" />
+ <install name="lib/Horde/Db/Adapter/Mysqli.php" as="Horde/Db/Adapter/Mysqli.php" />
+
+ <install name="lib/Horde/Db/Adapter/Oci8.php" as="Horde/Db/Adapter/Oci8.php" />
+
+ <install name="lib/Horde/Db/Adapter/Oracle/Schema.php" as="Horde/Db/Adapter/Oracle/Schema.php" />
+
+ <install name="lib/Horde/Db/Adapter/Pdo/Abstract.php" as="Horde/Db/Adapter/Pdo/Abstract.php" />
+ <install name="lib/Horde/Db/Adapter/Pdo/Mysql.php" as="Horde/Db/Adapter/Pdo/Mysql.php" />
+ <install name="lib/Horde/Db/Adapter/Pdo/Oci.php" as="Horde/Db/Adapter/Pdo/Oci.php" />
+ <install name="lib/Horde/Db/Adapter/Pdo/Pgsql.php" as="Horde/Db/Adapter/Pdo/Pgsql.php" />
+ <install name="lib/Horde/Db/Adapter/Pdo/Sqlite.php" as="Horde/Db/Adapter/Pdo/Sqlite.php" />
+
+ <install name="lib/Horde/Db/Adapter/Postgresql/Column.php" as="Horde/Db/Adapter/Postgresql/Column.php" />
+ <install name="lib/Horde/Db/Adapter/Postgresql/Schema.php" as="Horde/Db/Adapter/Postgresql/Schema.php" />
+
+ <install name="lib/Horde/Db/Adapter/Sqlite/Column.php" as="Horde/Db/Adapter/Sqlite/Column.php" />
+ <install name="lib/Horde/Db/Adapter/Sqlite/Schema.php" as="Horde/Db/Adapter/Sqlite/Schema.php" />
+
+ <install name="lib/Horde/Db/Adapter.php" as="Horde/Db/Adapter.php" />
+ <install name="lib/Horde/Db/Exception.php" as="Horde/Db/Exception.php" />
+
+ <install name="lib/Horde/Db.php" as="Horde/Db.php" />
+ </filelist>
+ </phprelease>
+</package>
--- /dev/null
+D/Horde////
--- /dev/null
+framework/Db/test
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+framework/Db/test/Horde
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+D/Mysql////
+D/Postgresql////
+D/Sqlite////
+D/Pdo////
+/MissingTest.php/1.1/Fri Sep 19 03:59:03 2008//
+/MysqliSuite.php/1.1/Fri Sep 19 03:59:03 2008//
+/MysqliTest.php/1.1/Fri Sep 19 03:59:03 2008//
--- /dev/null
+framework/Db/test/Horde/Db/Adapter
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_MissingTest extends PHPUnit_Framework_TestCase
+{
+ public function testMissingAdapter()
+ {
+ $this->markTestSkipped('The ' . $this->adapter . ' adapter is not available');
+ }
+
+}
--- /dev/null
+/ColumnDefinitionTest.php/1.1/Fri Sep 19 03:59:02 2008//
+/ColumnTest.php/1.1/Fri Sep 19 03:59:03 2008//
+/TableDefinitionTest.php/1.1/Fri Sep 19 03:59:03 2008//
+D
--- /dev/null
+framework/Db/test/Horde/Db/Adapter/Mysql
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Mysql_ColumnDefinitionTest extends PHPUnit_Framework_TestCase
+{
+ protected function setUp()
+ {
+ list($this->_conn,) = $this->sharedFixture->getConnection();
+ }
+
+ protected function tearDown()
+ {
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ public function testConstruct()
+ {
+ $col = new Horde_Db_Adapter_Abstract_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_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string'
+ );
+ $this->assertEquals('`col_name` varchar(255)', $col->toSql());
+ }
+
+ public function testToSqlLimit()
+ {
+ $col = new Horde_Db_Adapter_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string', 40
+ );
+ $this->assertEquals('`col_name` varchar(40)', $col->toSql());
+
+ // set attribute instead
+ $col = new Horde_Db_Adapter_Abstract_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_Abstract_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_Abstract_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_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string', null, null, null, null, false
+ );
+ $this->assertEquals('`col_name` varchar(255) NOT NULL', $col->toSql());
+
+ // set attribute instead
+ $col = new Horde_Db_Adapter_Abstract_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_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string', null, null, null, 'test', null
+ );
+ $this->assertEquals("`col_name` varchar(255) DEFAULT 'test'", $col->toSql());
+
+ // set attribute instead
+ $col = new Horde_Db_Adapter_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string'
+ );
+ $col->setDefault('test');
+ $this->assertEquals("`col_name` varchar(255) DEFAULT 'test'", $col->toSql());
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Mysql_ColumnTest extends PHPUnit_Framework_TestCase
+{
+ /*##########################################################################
+ # Construction
+ ##########################################################################*/
+
+ public function testDefaultNull()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)');
+ $this->assertEquals(true, $col->isNull());
+ }
+
+ public function testNotNull()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)', false);
+ $this->assertEquals(false, $col->isNull());
+ }
+
+ public function testName()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)');
+ $this->assertEquals('name', $col->isNull());
+ }
+
+ public function testSqlType()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)');
+ $this->assertEquals('varchar(255)', $col->getSqlType());
+ }
+
+ public function testIsText()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'varchar(255)');
+ $this->assertTrue($col->isText());
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'text');
+ $this->assertTrue($col->isText());
+
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'int(11)');
+ $this->assertFalse($col->isText());
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'float(11,1)');
+ $this->assertFalse($col->isText());
+ }
+
+ public function testIsNumber()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'varchar(255)');
+ $this->assertFalse($col->isNumber());
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'text');
+ $this->assertFalse($col->isNumber());
+
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'int(11)');
+ $this->assertTrue($col->isNumber());
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'float(11,1)');
+ $this->assertTrue($col->isNumber());
+ }
+
+
+ /*##########################################################################
+ # Types
+ ##########################################################################*/
+
+ public function testTypeInteger()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)');
+ $this->assertEquals('integer', $col->getType());
+ }
+
+ public function testTypeFloat()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'float(11,1)');
+ $this->assertEquals('float', $col->getType());
+ }
+
+ public function testTypeDecimalPrecisionNone()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'decimal(11,0)');
+ $this->assertEquals('integer', $col->getType());
+ }
+
+ public function testTypeDecimal()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'decimal(11,1)');
+ $this->assertEquals('decimal', $col->getType());
+ }
+
+ public function testTypeDatetime()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'datetime');
+ $this->assertEquals('datetime', $col->getType());
+ }
+
+ public function testTypeTimestamp()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'CURRENT_TIMESTAMP', 'timestamp');
+ $this->assertEquals('timestamp', $col->getType());
+ }
+
+ public function testTypeTime()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'time');
+ $this->assertEquals('time', $col->getType());
+ }
+
+ public function testTypeDate()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'date');
+ $this->assertEquals('date', $col->getType());
+ }
+
+ public function testTypeText()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'text');
+ $this->assertEquals('text', $col->getType());
+ }
+
+ public function testTypeBinary()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'blob(255)');
+ $this->assertEquals('binary', $col->getType());
+ }
+
+ public function testTypeString()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', 'NULL', 'varchar(255)');
+ $this->assertEquals('string', $col->getType());
+ }
+
+ /*##########################################################################
+ # Primary
+ ##########################################################################*/
+
+ public function testPrimary()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('id', 'NULL', 'int(11)');
+ $this->assertFalse($col->isPrimary());
+
+ $col->setPrimary(true);
+ $this->assertTrue($col->isPrimary());
+ }
+
+ /*##########################################################################
+ # Extract Limit
+ ##########################################################################*/
+
+ public function testExtractLimitInt()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'int(11)');
+ $this->assertEquals(11, $col->getLimit());
+ }
+
+ public function testExtractLimitVarchar()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'varchar(255)');
+ $this->assertEquals(255, $col->getLimit());
+ }
+
+ public function testExtractLimitDecimal()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'decimal(11,1)');
+ $this->assertEquals('11', $col->getLimit());
+ }
+
+ public function testExtractLimitText()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'text');
+ $this->assertEquals(null, $col->getLimit());
+ }
+
+ public function testExtractLimitNone()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL');
+ $this->assertEquals(null, $col->getLimit());
+ }
+
+ /*##########################################################################
+ # Extract Precision/Scale
+ ##########################################################################*/
+
+ public function testExtractPrecisionScale()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('test', 'NULL', 'decimal(12,1)');
+ $this->assertEquals('12', $col->precision());
+ $this->assertEquals('1', $col->scale());
+ }
+
+
+ /*##########################################################################
+ # Type Cast Values
+ ##########################################################################*/
+
+ public function testTypeCastInteger()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', '1', 'int(11)', false);
+ $this->assertEquals(1, $col->getDefault());
+ }
+
+ public function testTypeCastFloat()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('version', '1.0', 'float(11,1)', false);
+ $this->assertEquals(1.0, $col->getDefault());
+ }
+
+ public function testTypeCastString()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', 'n/a', 'varchar(255)', false);
+ $this->assertEquals('n/a', $col->getDefault());
+ }
+
+ public function testTypeCastBooleanFalse()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('is_active', '0', 'tinyint(1)', false);
+ $this->assertSame(false, $col->getDefault());
+ }
+
+ public function testTypeCastBooleanTrue()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('is_active', '1', 'tinyint(1)', false);
+ $this->assertSame(true, $col->getDefault());
+ }
+
+ /*##########################################################################
+ # Column Types
+ ##########################################################################*/
+
+ public function testColumnTypeEnum()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('user', 'NULL', "enum('derek', 'mike')");
+ $this->assertEquals('string', $col->getType());
+ }
+
+ public function testColumnTypeBoolean()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('is_active', 'NULL', 'tinyint(1)');
+ $this->assertEquals('boolean', $col->getType());
+ }
+
+
+ /*##########################################################################
+ # Defaults
+ ##########################################################################*/
+
+ public function testDefaultDatetime()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'datetime');
+ $this->assertEquals(null, $col->getDefault());
+ }
+
+ public function testDefaultInteger()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'int(11)');
+ $this->assertEquals(null, $col->getDefault());
+ }
+
+ public function testDefaultString()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'varchar(255)');
+ $this->assertEquals('', $col->getDefault());
+ }
+
+ public function testDefaultText()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'text');
+ $this->assertEquals('', $col->getDefault());
+ }
+
+ public function testDefaultBinary()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('name', '', 'blob(255)');
+ $this->assertEquals('', $col->getDefault());
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Mysql_TableDefinitionTest extends PHPUnit_Framework_TestCase
+{
+ protected function setUp()
+ {
+ list($this->_conn,) = $this->sharedFixture->getConnection();
+ }
+
+ protected function tearDown()
+ {
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ /*##########################################################################
+ # Public methods
+ ##########################################################################*/
+
+ public function testConstruct()
+ {
+ }
+
+ public function testName()
+ {
+ }
+
+ public function testGetOptions()
+ {
+ }
+
+ public function testPrimaryKey()
+ {
+ }
+
+ public function testColumn()
+ {
+ }
+
+ public function testToSql()
+ {
+ }
+
+ /*##########################################################################
+ # Array Access
+ ##########################################################################*/
+
+ public function testOffsetExists()
+ {
+ }
+
+ public function testOffsetGet()
+ {
+ }
+
+ public function testOffsetSet()
+ {
+ }
+
+ public function testOffsetUnset()
+ {
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_MysqliSuite extends PHPUnit_Framework_TestSuite
+{
+ public static function suite()
+ {
+ $suite = new self('Horde Framework - Horde_Db - MySQLi Adapter');
+
+ $skip = true;
+ if (extension_loaded('mysqli')) {
+ try {
+ list($conn,) = $suite->getConnection();
+ $skip = false;
+ $conn->disconnect();
+ } catch (Exception $e) {}
+ }
+
+ if ($skip) {
+ $skipTest = new Horde_Db_Adapter_MissingTest('testMissingAdapter');
+ $skipTest->adapter = 'MySQLi';
+ $suite->addTest($skipTest);
+ return $suite;
+ }
+
+ require_once dirname(__FILE__) . '/MysqliTest.php';
+ require_once dirname(__FILE__) . '/Mysql/ColumnTest.php';
+ require_once dirname(__FILE__) . '/Mysql/ColumnDefinitionTest.php';
+ require_once dirname(__FILE__) . '/Mysql/TableDefinitionTest.php';
+
+ $suite->addTestSuite('Horde_Db_Adapter_MysqliTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Mysql_ColumnTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Mysql_ColumnDefinitionTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Mysql_TableDefinitionTest');
+
+ return $suite;
+ }
+
+ public function getConnection()
+ {
+ if (!class_exists('CacheMock')) eval('class CacheMock { function get($key) { return $this->$key; } function set($key, $val) { $this->$key = $val; } } ?>');
+ $cache = new CacheMock;
+
+ $config = array(
+ 'adapter' => 'mysqli',
+ 'host' => 'localhost',
+ 'username' => '',
+ 'password' => '',
+ 'dbname' => 'test',
+ 'cache' => $cache,
+ );
+ if (isset($_ENV['HORDE_DB_TEST_DSN_MYSQLI']))
+ $config = array_merge($config, @json_decode($_ENV['HORDE_DB_TEST_DSN_MYSQLI'], true));
+
+ $conn = Horde_Db_Adapter::getInstance($config);
+ return array($conn, $cache);
+ }
+
+ protected function setUp()
+ {
+ $this->sharedFixture = $this;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_MysqliTest extends PHPUnit_Framework_TestCase
+{
+ // @todo - add logger instance
+ protected function setUp()
+ {
+ list($this->_conn, $this->_cache) = $this->sharedFixture->getConnection();
+
+ // clear out detritus from any previous test runs.
+ $this->_dropTestTables();
+
+ $table = $this->_conn->createTable('unit_tests');
+ $table->column('integer_value', 'integer', array('limit' => 11, 'default' => 0));
+ $table->column('string_value', 'string', array('limit' => 255, 'default' => ''));
+ $table->column('text_value', 'text', array('null' => false, 'default' => ''));
+ $table->column('float_value', 'float', array('precision' => 2, 'scale' => 1, 'default' => 0.0));
+ $table->column('decimal_value', 'decimal', array('precision' => 2, 'scale' => 1, 'default' => 0.0));
+ $table->column('datetime_value', 'datetime', array('default' => '0000-00-00 00:00:00'));
+ $table->column('date_value', 'date', array('default' => '0000-00-00'));
+ $table->column('time_value', 'time', array('default' => '00:00:00'));
+ $table->column('blob_value', 'binary', array('null' => false, 'default' => ''));
+ $table->column('boolean_value', 'boolean', array('default' => false));
+ $table->column('email_value', 'string', array('limit' => 255, 'default' => ''));
+ $table->end();
+ $this->_conn->addIndex('unit_tests', 'string_value', array('name' => 'string_value'));
+ $this->_conn->addIndex('unit_tests', 'integer_value', array('name' => 'integer_value', 'unique' => true));
+ $this->_conn->addIndex('unit_tests', array('integer_value', 'string_value'), array('name' => 'integer_string'));
+
+ // read sql file for statements
+ $statements = array();
+ $current_stmt = '';
+ $fp = fopen(dirname(__FILE__) . '/../fixtures/unit_tests.sql', 'r');
+ while ($line = fgets($fp, 8192)) {
+ $line = rtrim(preg_replace('/^(.*)--.*$/s', '\1', $line));
+ if (!$line) {
+ continue;
+ }
+
+ $current_stmt .= $line;
+
+ if (substr($line, -1) == ';') {
+ // leave off the ending ;
+ $statements[] = substr($current_stmt, 0, -1);
+ $current_stmt = '';
+ }
+ }
+
+ // run statements
+ foreach ($statements as $stmt) {
+ $this->_conn->execute($stmt);
+ }
+ }
+
+ protected function tearDown()
+ {
+ // clean up
+ $this->_dropTestTables();
+
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ /*##########################################################################
+ # Connection
+ ##########################################################################*/
+
+ public function testConnect()
+ {
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+ public function testDisconnect()
+ {
+ $this->_conn->disconnect();
+ $this->assertFalse($this->_conn->isActive());
+
+ $this->_conn->connect();
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+ public function testReconnect()
+ {
+ $this->_conn->reconnect();
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+
+ /*##########################################################################
+ # Accessor
+ ##########################################################################*/
+
+ public function testAdapterName()
+ {
+ $this->assertEquals('MySQLi', $this->_conn->adapterName());
+ }
+
+ public function testSupportsMigrations()
+ {
+ $this->assertTrue($this->_conn->supportsMigrations());
+ }
+
+ public function testSupportsCountDistinct()
+ {
+ $this->assertTrue($this->_conn->supportsCountDistinct());
+ }
+
+
+ /*##########################################################################
+ # Database Statements
+ ##########################################################################*/
+
+ public function testExecute()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->execute($sql);
+ $row = $result->fetch_assoc();
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelect()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->select($sql);
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectWithBoundParameters()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id=?";
+ $result = $this->_conn->select($sql, array(1));
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectWithBoundParametersQuotesString()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE string_value=?";
+ $result = $this->_conn->select($sql, array('name a'));
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectAll()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectAll($sql);
+ $this->assertType('array', $result);
+ $this->assertGreaterThan(0, count($result));
+ $this->assertEquals(1, $result[0]['id']);
+ }
+
+ public function testSelectOne()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectOne($sql);
+ $this->assertEquals(1, $result['id']);
+ }
+
+ public function testSelectValue()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectValue($sql);
+ $this->assertEquals(1, $result);
+ }
+
+ public function testSelectValues()
+ {
+ $sql = "SELECT * FROM unit_tests";
+ $result = $this->_conn->selectValues($sql);
+ $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result);
+ }
+
+ public function testInsert()
+ {
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $result = $this->_conn->insert($sql);
+
+ $this->assertEquals(7, $result);
+ }
+
+ public function testUpdate()
+ {
+ $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)";
+ $result = $this->_conn->update($sql);
+
+ $this->assertEquals(1, $result);
+ }
+
+ public function testDelete()
+ {
+ $sql = "DELETE FROM unit_tests WHERE id IN (1,2)";
+ $result = $this->_conn->delete($sql);
+
+ $this->assertEquals(2, $result);
+ }
+
+ public function testTransactionStarted()
+ {
+ $this->assertFalse($this->_conn->transactionStarted());
+ $this->_conn->beginDbTransaction();
+
+ $this->assertTrue($this->_conn->transactionStarted());
+ $this->_conn->commitDbTransaction();
+
+ $this->assertFalse($this->_conn->transactionStarted());
+ }
+
+ public function testTransactionCommit()
+ {
+ $this->_conn->beginDbTransaction();
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $this->_conn->insert($sql);
+ $this->_conn->commitDbTransaction();
+
+ // make sure it inserted
+ $sql = "SELECT integer_value FROM unit_tests WHERE id='7'";
+ $this->assertEquals('999', $this->_conn->selectValue($sql));
+ }
+
+ public function testTransactionRollback()
+ {
+ $this->_conn->beginDbTransaction();
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $this->_conn->insert($sql);
+ $this->_conn->rollbackDbTransaction();
+
+ // make sure it inserted
+ $sql = "SELECT integer_value FROM unit_tests WHERE id='7'";
+ $this->assertEquals(null, $this->_conn->selectValue($sql));
+ }
+
+
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ public function testQuoteNull()
+ {
+ $this->assertEquals('NULL', $this->_conn->quote(null));
+ }
+
+ public function testQuoteTrue()
+ {
+ $this->assertEquals('1', $this->_conn->quote(true));
+ }
+
+ public function testQuoteFalse()
+ {
+ $this->assertEquals('0', $this->_conn->quote(false));
+ }
+
+ public function testQuoteString()
+ {
+ $this->assertEquals("'my string'", $this->_conn->quote('my string'));
+ }
+
+ public function testQuoteDirtyString()
+ {
+ $this->assertEquals("'derek\'s string'", $this->_conn->quote('derek\'s string'));
+ }
+
+ public function testQuoteColumnName()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)');
+ $this->assertEquals('1', $this->_conn->quote(true, $col));
+ }
+
+
+ /*##########################################################################
+ # Schema Statements
+ ##########################################################################*/
+
+ public function testNativeDatabaseTypes()
+ {
+ $types = $this->_conn->nativeDatabaseTypes();
+ $this->assertEquals(array('name' => 'int', 'limit' => 11), $types['integer']);
+ }
+
+ public function testTableAliasLength()
+ {
+ $len = $this->_conn->tableAliasLength();
+ $this->assertEquals(255, $len);
+ }
+
+ public function testTableAliasFor()
+ {
+ $alias = $this->_conn->tableAliasFor('my_table_name');
+ $this->assertEquals('my_table_name', $alias);
+ }
+
+ public function testTables()
+ {
+ $tables = $this->_conn->tables();
+ $this->assertTrue(count($tables) > 0);
+ $this->assertContains('unit_tests', $tables);
+ }
+
+ public function testIndexes()
+ {
+ $indexes = $this->_conn->indexes('unit_tests');
+ $this->assertEquals(3, count($indexes));
+
+ // unique index
+ $col = array('integer_value');
+ $this->assertEquals('unit_tests', $indexes[0]->table);
+ $this->assertEquals('integer_value', $indexes[0]->name);
+ $this->assertEquals(true, $indexes[0]->unique);
+ $this->assertEquals($col, $indexes[0]->columns);
+
+ // normal index
+ $col = array('string_value');
+ $this->assertEquals('unit_tests', $indexes[1]->table);
+ $this->assertEquals('string_value', $indexes[1]->name);
+ $this->assertEquals(false, $indexes[1]->unique);
+ $this->assertEquals($col, $indexes[1]->columns);
+
+ // multi-column index
+ $col = array('integer_value', 'string_value');
+ $this->assertEquals('unit_tests', $indexes[2]->table);
+ $this->assertEquals('integer_string', $indexes[2]->name);
+ $this->assertEquals(false, $indexes[2]->unique);
+ $this->assertEquals($col, $indexes[2]->columns);
+ }
+
+ public function testColumns()
+ {
+ $columns = $this->_conn->columns('unit_tests');
+ $this->assertEquals(12, count($columns));
+
+ $col = $columns[0];
+ $this->assertEquals('id', $col->getName());
+ $this->assertEquals('integer', $col->getType());
+ $this->assertEquals(false, $col->isNull());
+ $this->assertEquals(11, $col->getLimit());
+ $this->assertEquals('', $col->getDefault());
+ $this->assertEquals('int(11)', $col->getSqlType());
+ $this->assertEquals(false, $col->isText());
+ $this->assertEquals(true, $col->isNumber());
+ }
+
+ public function testCreateTable()
+ {
+ $this->_createTestTable('sports');
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testCreateTableNoPk()
+ {
+ $this->_createTestTable('sports', array('primaryKey' => false));
+
+ try {
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertNull($this->_conn->selectValue($sql));
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Expected exception for no pk");
+ }
+
+ public function testCreateTableForce()
+ {
+ $this->_createTestTable('sports');
+ $this->_createTestTable('sports', array('force' => true));
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testCreateTableTemporary()
+ {
+ $this->_createTestTable('sports', array('temporary' => true));
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testRenameTable()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->renameTable('sports', 'my_sports');
+
+ $sql = "SELECT id FROM my_sports WHERE id = 1";
+ $this->assertEquals("1", $this->_conn->selectValue($sql));
+ }
+
+ public function testDropTable()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->dropTable('sports');
+
+ try {
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->_conn->execute($sql);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Table exists where it shouldn't have");
+ }
+
+ public function testAddColumn()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->addColumn('sports', 'modified_at', 'date');
+ $this->_conn->update("UPDATE sports SET modified_at = '2007-01-01'");
+
+ $sql = "SELECT modified_at FROM sports WHERE id = 1";
+ $this->assertEquals("2007-01-01", $this->_conn->selectValue($sql));
+ }
+
+ public function testRemoveColumn()
+ {
+ $this->_createTestTable('sports');
+ $sql = "SELECT name FROM sports WHERE id = 1";
+ $this->assertEquals("mlb", $this->_conn->selectValue($sql));
+
+ $this->_conn->removeColumn('sports', 'name');
+
+ try {
+ $sql = "SELECT name FROM sports WHERE id = 1";
+ $this->_conn->execute($sql);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Column exists where it shouldn't have");
+ }
+
+ public function testChangeColumnDefault()
+ {
+ $this->_createTestTable('sports');
+ $beforeChange = $this->_getColumn('sports', 'name');
+ $this->assertEquals('', $beforeChange->getDefault());
+
+ $this->_conn->changeColumnDefault('sports', 'name', 'test');
+
+ $afterChange = $this->_getColumn('sports', 'name');
+ $this->assertEquals('test', $afterChange->getDefault());
+ }
+
+ public function testChangeColumnType()
+ {
+ $this->_createTestTable('sports');
+ $beforeChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('tinyint(1)', $beforeChange->getSqlType());
+
+ $this->_conn->changeColumn('sports', 'is_college', 'string');
+
+ $afterChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('varchar(255)', $afterChange->getSqlType());
+ }
+
+ 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 testRenameColumn()
+ {
+ $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 testAddIndexDefault()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college');
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ }
+
+ public function testAddIndexMultiColumn()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', array('name', 'is_college'));
+
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNotNull($index);
+ }
+
+ public function testAddIndexUnique()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college', array('unique' => true));
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ $this->assertTrue($index->unique);
+ }
+
+ public function testAddIndexName()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college', array('name' => 'test'));
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ $this->assertEquals('test', $index->name);
+ }
+
+ public function testRemoveIndexSingleColumn()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', 'is_college');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('column' => 'is_college'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+ }
+
+ public function testRemoveIndexMultiColumn()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', array('name', 'is_college'));
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('column' => array('name', 'is_college')));
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNull($index);
+ }
+
+ public function testRemoveIndexByName()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', 'is_college', array('name' => 'test'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('name' => 'test'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+ }
+
+ public function testIndexNameInvalid()
+ {
+ try {
+ $name = $this->_conn->indexName('sports');
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail("Adding an index with crappy options worked where it shouldn't have");
+ }
+
+ public function testIndexNameBySingleColumn()
+ {
+ $name = $this->_conn->indexName('sports', array('column' => 'is_college'));
+ $this->assertEquals('index_sports_on_is_college', $name);
+ }
+
+ public function testIndexNameByMultiColumn()
+ {
+ $name = $this->_conn->indexName('sports', array('column' =>
+ array('name', 'is_college')));
+ $this->assertEquals('index_sports_on_name_and_is_college', $name);
+ }
+
+ public function testIndexNameByName()
+ {
+ $name = $this->_conn->indexName('sports', array('name' => 'test'));
+ $this->assertEquals('test', $name);
+ }
+
+ public function testStructureDump()
+ {
+ $this->_createTestTable('sports');
+
+ // single table
+ $structure = $this->_conn->structureDump('sports');
+
+ $expected = "CREATE TABLE `sports` (\n".
+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n".
+ " `name` varchar(255) DEFAULT NULL,\n".
+ " `is_college` tinyint(1) DEFAULT NULL,\n".
+ " PRIMARY KEY (`id`)\n".
+ ") ENGINE=InnoDB";
+
+ // MySQL differs in how it dumps table structure between versions, so do
+ // some normalization.
+ $expected = strtolower(preg_replace('/\s+/', ' ', $expected));
+ $structure = strtolower(preg_replace('/\s+/', ' ', $structure));
+
+ $this->assertContains($expected, $structure);
+
+ // entire structure
+ $structure = $this->_conn->structureDump();
+ $structure = strtolower(preg_replace('/\s+/', ' ', $structure));
+
+ // contains, but doesn't match only sports table
+ $this->assertContains($expected, $structure);
+ $this->assertNotEquals($expected, $structure);
+ }
+
+ public function testInitializeSchemaInformation()
+ {
+ $this->_conn->initializeSchemaInformation();
+
+ $sql = "SELECT version FROM schema_info";
+ $this->assertEquals(0, $this->_conn->selectValue($sql));
+ }
+
+ public function testTypeToSqlTypePrimaryKey()
+ {
+ $result = $this->_conn->typeToSql('primaryKey');
+ $this->assertEquals('int(11) DEFAULT 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('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', '11');
+ $this->assertEquals('int(11)', $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 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);
+ }
+
+
+ /*##########################################################################
+ # Test Cached table descriptions
+ ##########################################################################*/
+
+ public function testCachedTableDescription()
+ {
+ // remove any current cache.
+ $this->_cache->set('tables/cache_table', '');
+ $this->assertEquals('', $this->_cache->get('tables/cache_table'));
+
+ $this->_createTestTable('cache_table');
+ $cols = $this->_conn->columns('cache_table');
+
+ $this->assertNotEquals('', $this->_cache->get('tables/cache_table'));
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * Create table to perform tests on
+ */
+ protected function _createTestTable($name, $options=array())
+ {
+ $table = $this->_conn->createTable($name, $options);
+ $table->column('name', 'string');
+ $table->column('is_college', 'boolean');
+ $table->end();
+
+ try {
+ // make sure table was created
+ $sql = "INSERT INTO $name (id, name, is_college)
+ VALUES (1, 'mlb', 0)";
+ $this->_conn->insert($sql);
+ } catch (Exception $e) {}
+ }
+
+ /**
+ * drop test tables
+ */
+ protected function _dropTestTables()
+ {
+ try {
+ $this->_conn->dropTable('unit_tests');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('sports');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('my_sports');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('schema_info');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('cache_table');
+ } catch (Exception $e) {}
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+}
--- /dev/null
+/MysqlSuite.php/1.1/Fri Sep 19 03:59:02 2008//
+/MysqlTest.php/1.1/Fri Sep 19 03:59:02 2008//
+/PgsqlSuite.php/1.1/Fri Sep 19 03:59:02 2008//
+/PgsqlTest.php/1.1/Fri Sep 19 03:59:02 2008//
+/SqliteSuite.php/1.1/Fri Sep 19 03:59:02 2008//
+/SqliteTest.php/1.1/Fri Sep 19 03:59:02 2008//
+D
--- /dev/null
+framework/Db/test/Horde/Db/Adapter/Pdo
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Pdo_MysqlSuite extends PHPUnit_Framework_TestSuite
+{
+ public static function suite()
+ {
+ $suite = new self('Horde Framework - Horde_Db - PDO-MySQL Adapter');
+
+ $skip = true;
+ if (extension_loaded('pdo') && in_array('mysql', PDO::getAvailableDrivers())) {
+ try {
+ list($conn,) = $suite->getConnection();
+ $skip = false;
+ $conn->disconnect();
+ } catch (Exception $e) {}
+ }
+
+ if ($skip) {
+ $skipTest = new Horde_Db_Adapter_MissingTest('testMissingAdapter');
+ $skipTest->adapter = 'PDO_MySQL';
+ $suite->addTest($skipTest);
+ return $suite;
+ }
+
+ require_once dirname(__FILE__) . '/MysqlTest.php';
+ require_once dirname(__FILE__) . '/../Mysql/ColumnTest.php';
+ require_once dirname(__FILE__) . '/../Mysql/ColumnDefinitionTest.php';
+ require_once dirname(__FILE__) . '/../Mysql/TableDefinitionTest.php';
+
+ $suite->addTestSuite('Horde_Db_Adapter_Pdo_MysqlTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Mysql_ColumnTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Mysql_ColumnDefinitionTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Mysql_TableDefinitionTest');
+
+ return $suite;
+ }
+
+ public function getConnection()
+ {
+ if (!class_exists('CacheMock')) eval('class CacheMock { function get($key) { return $this->$key; } function set($key, $val) { $this->$key = $val; } } ?>');
+ $cache = new CacheMock;
+
+ $config = array(
+ 'adapter' => 'pdo_mysql',
+ 'host' => 'localhost',
+ 'username' => '',
+ 'password' => '',
+ 'dbname' => 'test',
+ 'cache' => $cache,
+ );
+ if (isset($_ENV['HORDE_DB_TEST_DSN_PDO_MYSQL']))
+ $config = array_merge($config, @json_decode($_ENV['HORDE_DB_TEST_DSN_PDO_MYSQL'], true));
+
+ $conn = Horde_Db_Adapter::getInstance($config);
+ return array($conn, $cache);
+ }
+
+ protected function setUp()
+ {
+ $this->sharedFixture = $this;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Pdo_MysqlTest extends PHPUnit_Framework_TestCase
+{
+ // @todo - add logger instance
+ protected function setUp()
+ {
+ list($this->_conn, $this->_cache) = $this->sharedFixture->getConnection();
+
+ // clear out detritus from any previous test runs.
+ $this->_dropTestTables();
+
+ $table = $this->_conn->createTable('unit_tests');
+ $table->column('integer_value', 'integer', array('limit' => 11, 'default' => 0));
+ $table->column('string_value', 'string', array('limit' => 255, 'default' => ''));
+ $table->column('text_value', 'text', array('null' => false, 'default' => ''));
+ $table->column('float_value', 'float', array('precision' => 2, 'scale' => 1, 'default' => 0.0));
+ $table->column('decimal_value', 'decimal', array('precision' => 2, 'scale' => 1, 'default' => 0.0));
+ $table->column('datetime_value', 'datetime', array('default' => '0000-00-00 00:00:00'));
+ $table->column('date_value', 'date', array('default' => '0000-00-00'));
+ $table->column('time_value', 'time', array('default' => '00:00:00'));
+ $table->column('blob_value', 'binary', array('null' => false, 'default' => ''));
+ $table->column('boolean_value', 'boolean', array('default' => false));
+ $table->column('email_value', 'string', array('limit' => 255, 'default' => ''));
+ $table->end();
+ $this->_conn->addIndex('unit_tests', 'string_value', array('name' => 'string_value'));
+ $this->_conn->addIndex('unit_tests', 'integer_value', array('name' => 'integer_value', 'unique' => true));
+ $this->_conn->addIndex('unit_tests', array('integer_value', 'string_value'), array('name' => 'integer_string'));
+
+ // read sql file for statements
+ $statements = array();
+ $current_stmt = '';
+ $fp = fopen(dirname(__FILE__) . '/../../fixtures/unit_tests.sql', 'r');
+ while ($line = fgets($fp, 8192)) {
+ $line = rtrim(preg_replace('/^(.*)--.*$/s', '\1', $line));
+ if (!$line) {
+ continue;
+ }
+
+ $current_stmt .= $line;
+
+ if (substr($line, -1) == ';') {
+ // leave off the ending ;
+ $statements[] = substr($current_stmt, 0, -1);
+ $current_stmt = '';
+ }
+ }
+
+ // run statements
+ foreach ($statements as $stmt) {
+ $this->_conn->execute($stmt);
+ }
+ }
+
+ protected function tearDown()
+ {
+ // clean up
+ $this->_dropTestTables();
+
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ /*##########################################################################
+ # Connection
+ ##########################################################################*/
+
+ public function testConnect()
+ {
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+ public function testDisconnect()
+ {
+ $this->_conn->disconnect();
+ $this->assertFalse($this->_conn->isActive());
+
+ $this->_conn->connect();
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+ public function testReconnect()
+ {
+ $this->_conn->reconnect();
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+
+ /*##########################################################################
+ # Accessor
+ ##########################################################################*/
+
+ public function testAdapterName()
+ {
+ $this->assertEquals('PDO_MySQL', $this->_conn->adapterName());
+ }
+
+ public function testSupportsMigrations()
+ {
+ $this->assertTrue($this->_conn->supportsMigrations());
+ }
+
+ public function testSupportsCountDistinct()
+ {
+ $this->assertTrue($this->_conn->supportsCountDistinct());
+ }
+
+
+ /*##########################################################################
+ # Database Statements
+ ##########################################################################*/
+
+ public function testExecute()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->execute($sql);
+ $row = $result->fetch();
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelect()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->select($sql);
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectWithBoundParameters()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id=?";
+ $result = $this->_conn->select($sql, array(1));
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectWithBoundParametersQuotesString()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE string_value=?";
+ $result = $this->_conn->select($sql, array('name a'));
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectAll()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectAll($sql);
+ $this->assertType('array', $result);
+ $this->assertGreaterThan(0, count($result));
+ $this->assertEquals(1, $result[0]['id']);
+ }
+
+ public function testSelectOne()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectOne($sql);
+ $this->assertEquals(1, $result['id']);
+ }
+
+ public function testSelectValue()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectValue($sql);
+ $this->assertEquals(1, $result);
+ }
+
+ public function testSelectValues()
+ {
+ $sql = "SELECT * FROM unit_tests";
+ $result = $this->_conn->selectValues($sql);
+ $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result);
+ }
+
+ public function testInsert()
+ {
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $result = $this->_conn->insert($sql);
+
+ $this->assertEquals(7, $result);
+ }
+
+ public function testUpdate()
+ {
+ $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)";
+ $result = $this->_conn->update($sql);
+
+ $this->assertEquals(1, $result);
+ }
+
+ public function testDelete()
+ {
+ $sql = "DELETE FROM unit_tests WHERE id IN (1,2)";
+ $result = $this->_conn->delete($sql);
+
+ $this->assertEquals(2, $result);
+ }
+
+ public function testTransactionStarted()
+ {
+ $this->assertFalse($this->_conn->transactionStarted());
+ $this->_conn->beginDbTransaction();
+
+ $this->assertTrue($this->_conn->transactionStarted());
+ $this->_conn->commitDbTransaction();
+
+ $this->assertFalse($this->_conn->transactionStarted());
+ }
+
+ public function testTransactionCommit()
+ {
+ $this->_conn->beginDbTransaction();
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $this->_conn->insert($sql);
+ $this->_conn->commitDbTransaction();
+
+ // make sure it inserted
+ $sql = "SELECT integer_value FROM unit_tests WHERE id='7'";
+ $this->assertEquals('999', $this->_conn->selectValue($sql));
+ }
+
+ public function testTransactionRollback()
+ {
+ $this->_conn->beginDbTransaction();
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $this->_conn->insert($sql);
+ $this->_conn->rollbackDbTransaction();
+
+ // make sure it inserted
+ $sql = "SELECT integer_value FROM unit_tests WHERE id='7'";
+ $this->assertEquals(null, $this->_conn->selectValue($sql));
+ }
+
+
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ public function testQuoteNull()
+ {
+ $this->assertEquals('NULL', $this->_conn->quote(null));
+ }
+
+ public function testQuoteTrue()
+ {
+ $this->assertEquals('1', $this->_conn->quote(true));
+ }
+
+ public function testQuoteFalse()
+ {
+ $this->assertEquals('0', $this->_conn->quote(false));
+ }
+
+ public function testQuoteString()
+ {
+ $this->assertEquals("'my string'", $this->_conn->quote('my string'));
+ }
+
+ public function testQuoteDirtyString()
+ {
+ $this->assertEquals("'derek\'s string'", $this->_conn->quote('derek\'s string'));
+ }
+
+ public function testQuoteColumnName()
+ {
+ $col = new Horde_Db_Adapter_Mysql_Column('age', 'NULL', 'int(11)');
+ $this->assertEquals('1', $this->_conn->quote(true, $col));
+ }
+
+
+ /*##########################################################################
+ # Schema Statements
+ ##########################################################################*/
+
+ public function testNativeDatabaseTypes()
+ {
+ $types = $this->_conn->nativeDatabaseTypes();
+ $this->assertEquals(array('name' => 'int', 'limit' => 11), $types['integer']);
+ }
+
+ public function testTableAliasLength()
+ {
+ $len = $this->_conn->tableAliasLength();
+ $this->assertEquals(255, $len);
+ }
+
+ public function testTableAliasFor()
+ {
+ $alias = $this->_conn->tableAliasFor('my_table_name');
+ $this->assertEquals('my_table_name', $alias);
+ }
+
+ public function testTables()
+ {
+ $tables = $this->_conn->tables();
+ $this->assertTrue(count($tables) > 0);
+ $this->assertContains('unit_tests', $tables);
+ }
+
+ public function testIndexes()
+ {
+ $indexes = $this->_conn->indexes('unit_tests');
+ $this->assertEquals(3, count($indexes));
+
+ // unique index
+ $col = array('integer_value');
+ $this->assertEquals('unit_tests', $indexes[0]->table);
+ $this->assertEquals('integer_value', $indexes[0]->name);
+ $this->assertEquals(true, $indexes[0]->unique);
+ $this->assertEquals($col, $indexes[0]->columns);
+
+ // normal index
+ $col = array('string_value');
+ $this->assertEquals('unit_tests', $indexes[1]->table);
+ $this->assertEquals('string_value', $indexes[1]->name);
+ $this->assertEquals(false, $indexes[1]->unique);
+ $this->assertEquals($col, $indexes[1]->columns);
+
+ // multi-column index
+ $col = array('integer_value', 'string_value');
+ $this->assertEquals('unit_tests', $indexes[2]->table);
+ $this->assertEquals('integer_string', $indexes[2]->name);
+ $this->assertEquals(false, $indexes[2]->unique);
+ $this->assertEquals($col, $indexes[2]->columns);
+ }
+
+ public function testColumns()
+ {
+ $columns = $this->_conn->columns('unit_tests');
+ $this->assertEquals(12, count($columns));
+
+ $col = $columns[0];
+ $this->assertEquals('id', $col->getName());
+ $this->assertEquals('integer', $col->getType());
+ $this->assertEquals(false, $col->isNull());
+ $this->assertEquals(11, $col->getLimit());
+ $this->assertEquals('', $col->getDefault());
+ $this->assertEquals('int(11)', $col->getSqlType());
+ $this->assertEquals(false, $col->isText());
+ $this->assertEquals(true, $col->isNumber());
+ }
+
+ public function testCreateTable()
+ {
+ $this->_createTestTable('sports');
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testCreateTableNoPk()
+ {
+ $this->_createTestTable('sports', array('primaryKey' => false));
+
+ try {
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertNull($this->_conn->selectValue($sql));
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Expected exception for no pk");
+ }
+
+ public function testCreateTableForce()
+ {
+ $this->_createTestTable('sports');
+ $this->_createTestTable('sports', array('force' => true));
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testCreateTableTemporary()
+ {
+ $this->_createTestTable('sports', array('temporary' => true));
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testRenameTable()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->renameTable('sports', 'my_sports');
+
+ $sql = "SELECT id FROM my_sports WHERE id = 1";
+ $this->assertEquals("1", $this->_conn->selectValue($sql));
+ }
+
+ public function testDropTable()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->dropTable('sports');
+
+ try {
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->_conn->execute($sql);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Table exists where it shouldn't have");
+ }
+
+ public function testAddColumn()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->addColumn('sports', 'modified_at', 'date');
+ $this->_conn->update("UPDATE sports SET modified_at = '2007-01-01'");
+
+ $sql = "SELECT modified_at FROM sports WHERE id = 1";
+ $this->assertEquals("2007-01-01", $this->_conn->selectValue($sql));
+ }
+
+ public function testRemoveColumn()
+ {
+ $this->_createTestTable('sports');
+ $sql = "SELECT name FROM sports WHERE id = 1";
+ $this->assertEquals("mlb", $this->_conn->selectValue($sql));
+
+ $this->_conn->removeColumn('sports', 'name');
+
+ try {
+ $sql = "SELECT name FROM sports WHERE id = 1";
+ $this->_conn->execute($sql);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Column exists where it shouldn't have");
+ }
+
+ public function testChangeColumnDefault()
+ {
+ $this->_createTestTable('sports');
+ $beforeChange = $this->_getColumn('sports', 'name');
+ $this->assertEquals('', $beforeChange->getDefault());
+
+ $this->_conn->changeColumnDefault('sports', 'name', 'test');
+
+ $afterChange = $this->_getColumn('sports', 'name');
+ $this->assertEquals('test', $afterChange->getDefault());
+ }
+
+ public function testChangeColumnType()
+ {
+ $this->_createTestTable('sports');
+ $beforeChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('tinyint(1)', $beforeChange->getSqlType());
+
+ $this->_conn->changeColumn('sports', 'is_college', 'string');
+
+ $afterChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('varchar(255)', $afterChange->getSqlType());
+ }
+
+ 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 testRenameColumn()
+ {
+ $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 testAddIndexDefault()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college');
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ }
+
+ public function testAddIndexMultiColumn()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', array('name', 'is_college'));
+
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNotNull($index);
+ }
+
+ public function testAddIndexUnique()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college', array('unique' => true));
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ $this->assertTrue($index->unique);
+ }
+
+ public function testAddIndexName()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college', array('name' => 'test'));
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ $this->assertEquals('test', $index->name);
+ }
+
+ public function testRemoveIndexSingleColumn()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', 'is_college');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('column' => 'is_college'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+ }
+
+ public function testRemoveIndexMultiColumn()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', array('name', 'is_college'));
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('column' => array('name', 'is_college')));
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNull($index);
+ }
+
+ public function testRemoveIndexByName()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', 'is_college', array('name' => 'test'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('name' => 'test'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+ }
+
+ public function testIndexNameInvalid()
+ {
+ try {
+ $name = $this->_conn->indexName('sports');
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail("Adding an index with crappy options worked where it shouldn't have");
+ }
+
+ public function testIndexNameBySingleColumn()
+ {
+ $name = $this->_conn->indexName('sports', array('column' => 'is_college'));
+ $this->assertEquals('index_sports_on_is_college', $name);
+ }
+
+ public function testIndexNameByMultiColumn()
+ {
+ $name = $this->_conn->indexName('sports', array('column' =>
+ array('name', 'is_college')));
+ $this->assertEquals('index_sports_on_name_and_is_college', $name);
+ }
+
+ public function testIndexNameByName()
+ {
+ $name = $this->_conn->indexName('sports', array('name' => 'test'));
+ $this->assertEquals('test', $name);
+ }
+
+ public function testStructureDump()
+ {
+ $this->_createTestTable('sports');
+
+ // single table
+ $structure = $this->_conn->structureDump('sports');
+
+ $expected = "CREATE TABLE `sports` (\n".
+ " `id` int(11) NOT NULL AUTO_INCREMENT,\n".
+ " `name` varchar(255) DEFAULT NULL,\n".
+ " `is_college` tinyint(1) DEFAULT NULL,\n".
+ " PRIMARY KEY (`id`)\n".
+ ") ENGINE=InnoDB";
+
+ // MySQL differs in how it dumps table structure between versions, so do
+ // some normalization.
+ $expected = strtolower(preg_replace('/\s+/', ' ', $expected));
+ $structure = strtolower(preg_replace('/\s+/', ' ', $structure));
+
+ $this->assertContains($expected, $structure);
+
+ // entire structure
+ $structure = $this->_conn->structureDump();
+ $structure = strtolower(preg_replace('/\s+/', ' ', $structure));
+
+ // contains, but doesn't match only sports table
+ $this->assertContains($expected, $structure);
+ $this->assertNotEquals($expected, $structure);
+ }
+
+ public function testInitializeSchemaInformation()
+ {
+ $this->_conn->initializeSchemaInformation();
+
+ $sql = "SELECT version FROM schema_info";
+ $this->assertEquals(0, $this->_conn->selectValue($sql));
+ }
+
+ public function testTypeToSqlTypePrimaryKey()
+ {
+ $result = $this->_conn->typeToSql('primaryKey');
+ $this->assertEquals('int(11) DEFAULT 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('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', '11');
+ $this->assertEquals('int(11)', $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 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);
+ }
+
+
+ /*##########################################################################
+ # Test Cached table descriptions
+ ##########################################################################*/
+
+ public function testCachedTableDescription()
+ {
+ // remove any current cache.
+ $this->_cache->set('tables/cache_table', '');
+ $this->assertEquals('', $this->_cache->get('tables/cache_table'));
+
+ $this->_createTestTable('cache_table');
+ $cols = $this->_conn->columns('cache_table');
+
+ $this->assertNotEquals('', $this->_cache->get('tables/cache_table'));
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * Create table to perform tests on
+ */
+ protected function _createTestTable($name, $options=array())
+ {
+ $table = $this->_conn->createTable($name, $options);
+ $table->column('name', 'string');
+ $table->column('is_college', 'boolean');
+ $table->end();
+
+ try {
+ // make sure table was created
+ $sql = "INSERT INTO $name (id, name, is_college)
+ VALUES (1, 'mlb', 0)";
+ $this->_conn->insert($sql);
+ } catch (Exception $e) {}
+ }
+
+ /**
+ * drop test tables
+ */
+ protected function _dropTestTables()
+ {
+ try {
+ $this->_conn->dropTable('unit_tests');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('sports');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('my_sports');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('schema_info');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('cache_table');
+ } catch (Exception $e) {}
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Pdo_PgsqlSuite extends PHPUnit_Framework_TestSuite
+{
+ public static function suite()
+ {
+ $suite = new self('Horde Framework - Horde_Db - PDO-PostgreSQL Adapter');
+
+ $skip = true;
+ if (extension_loaded('pdo') && in_array('pgsql', PDO::getAvailableDrivers())) {
+ try {
+ list($conn,) = $suite->getConnection();
+ $skip = false;
+ $conn->disconnect();
+ } catch (Exception $e) {}
+ }
+
+ if ($skip) {
+ $skipTest = new Horde_Db_Adapter_MissingTest('testMissingAdapter');
+ $skipTest->adapter = 'PDO_PostgreSQL';
+ $suite->addTest($skipTest);
+ return $suite;
+ }
+
+ require_once dirname(__FILE__) . '/PgsqlTest.php';
+ require_once dirname(__FILE__) . '/../Postgresql/ColumnTest.php';
+ require_once dirname(__FILE__) . '/../Postgresql/ColumnDefinitionTest.php';
+ require_once dirname(__FILE__) . '/../Postgresql/TableDefinitionTest.php';
+
+ $suite->addTestSuite('Horde_Db_Adapter_Pdo_PgsqlTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Postgresql_ColumnTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Postgresql_ColumnDefinitionTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Postgresql_TableDefinitionTest');
+
+ return $suite;
+ }
+
+ public function getConnection()
+ {
+ if (!class_exists('CacheMock')) eval('class CacheMock { function get($key) { return $this->$key; } function set($key, $val) { $this->$key = $val; } } ?>');
+ $cache = new CacheMock;
+
+ $config = array(
+ 'adapter' => 'pdo_pgsql',
+ 'username' => '',
+ 'password' => '',
+ 'dbname' => 'test',
+ 'cache' => $cache,
+ );
+ if (isset($_ENV['HORDE_DB_TEST_DSN_PDO_PGSQL']))
+ $config = array_merge($config, @json_decode($_ENV['HORDE_DB_TEST_DSN_PDO_PGSQL'], true));
+
+ $conn = Horde_Db_Adapter::getInstance($config);
+ return array($conn, $cache);
+ }
+
+ protected function setUp()
+ {
+ $this->sharedFixture = $this;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Pdo_PgsqlTest extends PHPUnit_Framework_TestCase
+{
+ // @todo - add logger instance
+ protected function setUp()
+ {
+ list($this->_conn, $this->_cache) = $this->sharedFixture->getConnection();
+
+ // clear out detritus from any previous test runs.
+ $this->_dropTestTables();
+
+ $table = $this->_conn->createTable('unit_tests');
+ $table->column('integer_value', 'integer', array('limit' => 11, 'default' => 0));
+ $table->column('string_value', 'string', array('limit' => 255, 'default' => ''));
+ $table->column('text_value', 'text', array('null' => false, 'default' => ''));
+ $table->column('float_value', 'float', array('precision' => 2, 'scale' => 1, 'default' => 0.0));
+ $table->column('decimal_value', 'decimal', array('precision' => 2, 'scale' => 1, 'default' => 0.0));
+ $table->column('datetime_value', 'datetime', array());
+ $table->column('date_value', 'date', array());
+ $table->column('time_value', 'time', array('default' => '00:00:00'));
+ $table->column('blob_value', 'binary', array('null' => false, 'default' => ''));
+ $table->column('boolean_value', 'boolean', array('default' => false));
+ $table->column('email_value', 'string', array('limit' => 255, 'default' => ''));
+ $table->end();
+ $this->_conn->addIndex('unit_tests', 'string_value', array('name' => 'string_value'));
+ $this->_conn->addIndex('unit_tests', 'integer_value', array('name' => 'integer_value', 'unique' => true));
+ $this->_conn->addIndex('unit_tests', array('integer_value', 'string_value'), array('name' => 'integer_string'));
+
+ // read sql file for statements
+ $statements = array();
+ $current_stmt = '';
+ $fp = fopen(dirname(__FILE__) . '/../../fixtures/unit_tests.sql', 'r');
+ while ($line = fgets($fp, 8192)) {
+ $line = rtrim(preg_replace('/^(.*)--.*$/s', '\1', $line));
+ if (!$line) {
+ continue;
+ }
+
+ $current_stmt .= $line;
+
+ if (substr($line, -1) == ';') {
+ // leave off the ending ;
+ $statements[] = substr($current_stmt, 0, -1);
+ $current_stmt = '';
+ }
+ }
+
+ // run statements
+ foreach ($statements as $stmt) {
+ $this->_conn->execute($stmt);
+ }
+ }
+
+ protected function tearDown()
+ {
+ // clean up
+ $this->_dropTestTables();
+
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ /*##########################################################################
+ # Connection
+ ##########################################################################*/
+
+ public function testConnect()
+ {
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+ public function testDisconnect()
+ {
+ $this->_conn->disconnect();
+ $this->assertFalse($this->_conn->isActive());
+
+ $this->_conn->connect();
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+ public function testReconnect()
+ {
+ $this->_conn->reconnect();
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+
+ /*##########################################################################
+ # Accessor
+ ##########################################################################*/
+
+ public function testAdapterName()
+ {
+ $this->assertEquals('PDO_PostgreSQL', $this->_conn->adapterName());
+ }
+
+ public function testSupportsMigrations()
+ {
+ $this->assertTrue($this->_conn->supportsMigrations());
+ }
+
+ public function testSupportsCountDistinct()
+ {
+ $this->assertTrue($this->_conn->supportsCountDistinct());
+ }
+
+
+ /*##########################################################################
+ # Database Statements
+ ##########################################################################*/
+
+ public function testExecute()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->execute($sql);
+ $row = $result->fetch();
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelect()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->select($sql);
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectWithBoundParameters()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id=?";
+ $result = $this->_conn->select($sql, array(1));
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectWithBoundParametersQuotesString()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE string_value=?";
+ $result = $this->_conn->select($sql, array('name a'));
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectAll()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectAll($sql);
+ $this->assertType('array', $result);
+ $this->assertGreaterThan(0, count($result));
+ $this->assertEquals(1, $result[0]['id']);
+ }
+
+ public function testSelectOne()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectOne($sql);
+ $this->assertEquals(1, $result['id']);
+ }
+
+ public function testSelectValue()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectValue($sql);
+ $this->assertEquals(1, $result);
+ }
+
+ public function testSelectValues()
+ {
+ $sql = "SELECT * FROM unit_tests";
+ $result = $this->_conn->selectValues($sql);
+ $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result);
+ }
+
+ public function testInsert()
+ {
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $result = $this->_conn->insert($sql);
+
+ $this->assertEquals(7, $result);
+ }
+
+ public function testUpdate()
+ {
+ $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)";
+ $result = $this->_conn->update($sql);
+
+ $this->assertEquals(1, $result);
+ }
+
+ public function testDelete()
+ {
+ $sql = "DELETE FROM unit_tests WHERE id IN (1,2)";
+ $result = $this->_conn->delete($sql);
+
+ $this->assertEquals(2, $result);
+ }
+
+ public function testTransactionStarted()
+ {
+ $this->assertFalse($this->_conn->transactionStarted());
+ $this->_conn->beginDbTransaction();
+
+ $this->assertTrue($this->_conn->transactionStarted());
+ $this->_conn->commitDbTransaction();
+
+ $this->assertFalse($this->_conn->transactionStarted());
+ }
+
+ public function testTransactionCommit()
+ {
+ $this->_conn->beginDbTransaction();
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $this->_conn->insert($sql);
+ $this->_conn->commitDbTransaction();
+
+ // make sure it inserted
+ $sql = "SELECT integer_value FROM unit_tests WHERE id='7'";
+ $this->assertEquals('999', $this->_conn->selectValue($sql));
+ }
+
+ public function testTransactionRollback()
+ {
+ $this->_conn->beginDbTransaction();
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $this->_conn->insert($sql);
+ $this->_conn->rollbackDbTransaction();
+
+ // make sure it inserted
+ $sql = "SELECT integer_value FROM unit_tests WHERE id='7'";
+ $this->assertEquals(null, $this->_conn->selectValue($sql));
+ }
+
+
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ public function testQuoteNull()
+ {
+ $this->assertEquals('NULL', $this->_conn->quote(null));
+ }
+
+ public function testQuoteTrue()
+ {
+ $this->assertEquals("'t'", $this->_conn->quote(true));
+ }
+
+ public function testQuoteFalse()
+ {
+ $this->assertEquals("'f'", $this->_conn->quote(false));
+ }
+
+ public function testQuoteString()
+ {
+ $this->assertEquals("'my string'", $this->_conn->quote('my string'));
+ }
+
+ public function testQuoteDirtyString()
+ {
+ $this->assertEquals("'derek''s string'", $this->_conn->quote('derek\'s string'));
+ }
+
+ public function testQuoteColumnName()
+ {
+ $col = new Horde_Db_Adapter_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 testTableAliasFor()
+ {
+ $alias = $this->_conn->tableAliasFor('my_table_name');
+ $this->assertEquals('my_table_name', $alias);
+ }
+
+ public function testSchemaSearchPath()
+ {
+ $schemaSearchPath = $this->_conn->getSchemaSearchPath();
+ $this->assertGreaterThan(0, strlen($schemaSearchPath));
+ }
+
+ public function testTables()
+ {
+ $tables = $this->_conn->tables();
+ $this->assertTrue(count($tables) > 0);
+ $this->assertContains('unit_tests', $tables);
+ }
+
+ public function testIndexes()
+ {
+ $indexes = $this->_conn->indexes('unit_tests');
+ $this->assertEquals(3, count($indexes));
+
+ // sort by name so we can predict the order of indexes
+ usort($indexes, create_function('$a, $b', 'return strcmp($a->name, $b->name);'));
+
+ // multi-column index
+ $col = array('integer_value', 'string_value');
+ $this->assertEquals('unit_tests', $indexes[0]->table);
+ $this->assertEquals('integer_string', $indexes[0]->name);
+ $this->assertEquals(false, $indexes[0]->unique);
+ $this->assertEquals($col, $indexes[0]->columns);
+
+ // unique index
+ $col = array('integer_value');
+ $this->assertEquals('unit_tests', $indexes[1]->table);
+ $this->assertEquals('integer_value', $indexes[1]->name);
+ $this->assertEquals(true, $indexes[1]->unique);
+ $this->assertEquals($col, $indexes[1]->columns);
+
+ // normal index
+ $col = array('string_value');
+ $this->assertEquals('unit_tests', $indexes[2]->table);
+ $this->assertEquals('string_value', $indexes[2]->name);
+ $this->assertEquals(false, $indexes[2]->unique);
+ $this->assertEquals($col, $indexes[2]->columns);
+ }
+
+ public function testColumns()
+ {
+ $columns = $this->_conn->columns('unit_tests');
+ $this->assertEquals(12, count($columns));
+
+ $col = $columns[0];
+ $this->assertEquals('id', $col->getName());
+ $this->assertEquals('integer', $col->getType());
+ $this->assertEquals(false, $col->isNull());
+ $this->assertEquals(null, $col->getLimit());
+ $this->assertEquals('', $col->getDefault());
+ $this->assertEquals('integer', $col->getSqlType());
+ $this->assertEquals(false, $col->isText());
+ $this->assertEquals(true, $col->isNumber());
+ }
+
+ public function testCreateTable()
+ {
+ $this->_createTestTable('sports');
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testCreateTableNoPk()
+ {
+ $this->_createTestTable('sports', array('primaryKey' => false));
+
+ try {
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertNull($this->_conn->selectValue($sql));
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Expected exception for no pk");
+ }
+
+ public function testCreateTableForce()
+ {
+ $this->_createTestTable('sports');
+ $this->_createTestTable('sports', array('force' => true));
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testCreateTableTemporary()
+ {
+ $this->_createTestTable('sports', array('temporary' => true));
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testRenameTable()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->renameTable('sports', 'my_sports');
+
+ $sql = "SELECT id FROM my_sports WHERE id = 1";
+ $this->assertEquals("1", $this->_conn->selectValue($sql));
+ }
+
+ public function testDropTable()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->dropTable('sports');
+
+ try {
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->_conn->execute($sql);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Table exists where it shouldn't have");
+ }
+
+ public function testAddColumn()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->addColumn('sports', 'modified_at', 'date');
+ $this->_conn->update("UPDATE sports SET modified_at = '2007-01-01'");
+
+ $sql = "SELECT modified_at FROM sports WHERE id = 1";
+ $this->assertEquals("2007-01-01", $this->_conn->selectValue($sql));
+ }
+
+ public function testRemoveColumn()
+ {
+ $this->_createTestTable('sports');
+ $sql = "SELECT name FROM sports WHERE id = 1";
+ $this->assertEquals("mlb", $this->_conn->selectValue($sql));
+
+ $this->_conn->removeColumn('sports', 'name');
+
+ try {
+ $sql = "SELECT name FROM sports WHERE id = 1";
+ $this->_conn->execute($sql);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Column exists where it shouldn't have");
+ }
+
+ public function testChangeColumnDefault()
+ {
+ $this->_createTestTable('sports');
+ $beforeChange = $this->_getColumn('sports', 'name');
+ $this->assertEquals('', $beforeChange->getDefault());
+
+ $this->_conn->changeColumnDefault('sports', 'name', 'test');
+
+ $afterChange = $this->_getColumn('sports', 'name');
+ $this->assertEquals('test', $afterChange->getDefault());
+ }
+
+ public function testChangeColumnType()
+ {
+ $this->_createTestTable('sports');
+ $beforeChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('boolean', $beforeChange->getSqlType());
+
+ $this->_conn->changeColumn('sports', 'is_college', 'string');
+
+ $afterChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('character varying(255)', $afterChange->getSqlType());
+ }
+
+ 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->_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 testAddIndexDefault()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college');
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ }
+
+ public function testAddIndexMultiColumn()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', array('name', 'is_college'));
+
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNotNull($index);
+ }
+
+ public function testAddIndexUnique()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college', array('unique' => true));
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ $this->assertTrue($index->unique);
+ }
+
+ public function testAddIndexName()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college', array('name' => 'test'));
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ $this->assertEquals('test', $index->name);
+ }
+
+ public function testRemoveIndexSingleColumn()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', 'is_college');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('column' => 'is_college'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+ }
+
+ public function testRemoveIndexMultiColumn()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', array('name', 'is_college'));
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('column' => array('name', 'is_college')));
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNull($index);
+ }
+
+ public function testRemoveIndexByName()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', 'is_college', array('name' => 'test'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('name' => 'test'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+ }
+
+ public function testIndexNameInvalid()
+ {
+ try {
+ $name = $this->_conn->indexName('sports');
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail("Adding an index with crappy options worked where it shouldn't have");
+ }
+
+ public function testIndexNameBySingleColumn()
+ {
+ $name = $this->_conn->indexName('sports', array('column' => 'is_college'));
+ $this->assertEquals('index_sports_on_is_college', $name);
+ }
+
+ public function testIndexNameByMultiColumn()
+ {
+ $name = $this->_conn->indexName('sports', array('column' =>
+ array('name', 'is_college')));
+ $this->assertEquals('index_sports_on_name_and_is_college', $name);
+ }
+
+ public function testIndexNameByName()
+ {
+ $name = $this->_conn->indexName('sports', array('name' => 'test'));
+ $this->assertEquals('test', $name);
+ }
+
+ public function testInitializeSchemaInformation()
+ {
+ $this->_conn->initializeSchemaInformation();
+
+ $sql = "SELECT version FROM schema_info";
+ $this->assertEquals(0, $this->_conn->selectValue($sql));
+ }
+
+ public function testTypeToSqlTypePrimaryKey()
+ {
+ $result = $this->_conn->typeToSql('primaryKey');
+ $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 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);
+ }
+
+
+ /*##########################################################################
+ # Test Cached table descriptions
+ ##########################################################################*/
+
+ public function testCachedTableDescription()
+ {
+ // remove any current cache.
+ $this->_cache->set('tables/cache_table', '');
+ $this->assertEquals('', $this->_cache->get('tables/cache_table'));
+
+ $this->_createTestTable('cache_table');
+ $cols = $this->_conn->columns('cache_table');
+
+ $this->assertNotEquals('', $this->_cache->get('tables/cache_table'));
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * Create table to perform tests on
+ */
+ protected function _createTestTable($name, $options=array())
+ {
+ $table = $this->_conn->createTable($name, $options);
+ $table->column('name', 'string');
+ $table->column('is_college', 'boolean');
+ $table->end();
+
+ try {
+ // make sure table was created
+ $sql = "INSERT INTO $name (id, name, is_college)
+ VALUES (1, 'mlb', 'f')";
+ $this->_conn->insert($sql);
+ } catch (Exception $e) {}
+ }
+
+ /**
+ * drop test tables
+ */
+ protected function _dropTestTables()
+ {
+ try {
+ $this->_conn->dropTable('unit_tests');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('sports');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('my_sports');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('schema_info');
+ } catch (Exception $e) {}
+ try {
+ $this->_conn->dropTable('cache_table');
+ } catch (Exception $e) {}
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Pdo_SqliteSuite extends PHPUnit_Framework_TestSuite
+{
+ public static function suite()
+ {
+ $suite = new self('Horde Framework - Horde_Db - PDO-SQLite Adapter');
+
+ $skip = true;
+ if (extension_loaded('pdo') && in_array('sqlite', PDO::getAvailableDrivers())) {
+ try {
+ list($conn,) = $suite->getConnection();
+ $skip = false;
+ $conn->disconnect();
+ } catch (Exception $e) {}
+ }
+
+ if ($skip) {
+ $skipTest = new Horde_Db_Adapter_MissingTest('testMissingAdapter');
+ $skipTest->adapter = 'PDO_SQLite';
+ $suite->addTest($skipTest);
+ return $suite;
+ }
+
+ require_once dirname(__FILE__) . '/SqliteTest.php';
+ require_once dirname(__FILE__) . '/../Sqlite/ColumnTest.php';
+ require_once dirname(__FILE__) . '/../Sqlite/ColumnDefinitionTest.php';
+ require_once dirname(__FILE__) . '/../Sqlite/TableDefinitionTest.php';
+
+ $suite->addTestSuite('Horde_Db_Adapter_Pdo_SqliteTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Sqlite_ColumnTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Sqlite_ColumnDefinitionTest');
+ $suite->addTestSuite('Horde_Db_Adapter_Sqlite_TableDefinitionTest');
+
+ return $suite;
+ }
+
+ public function getConnection()
+ {
+ if (!class_exists('CacheMock')) eval('class CacheMock { function get($key) { return $this->$key; } function set($key, $val) { $this->$key = $val; } } ?>');
+ $cache = new CacheMock;
+
+ $conn = Horde_Db_Adapter::getInstance(array(
+ 'adapter' => 'pdo_sqlite',
+ 'dbname' => ':memory:',
+ 'cache' => $cache,
+ ));
+
+ return array($conn, $cache);
+ }
+
+ protected function setUp()
+ {
+ $this->sharedFixture = $this;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Pdo_SqliteTest extends PHPUnit_Framework_TestCase
+{
+ // @todo - add logger instance
+ protected function setUp()
+ {
+ list($this->_conn, $this->_cache) = $this->sharedFixture->getConnection();
+
+ $table = $this->_conn->createTable('unit_tests');
+ $table->column('integer_value', 'integer', array('limit' => 11, 'default' => 0));
+ $table->column('string_value', 'string', array('limit' => 255, 'default' => ''));
+ $table->column('text_value', 'text', array('null' => false, 'default' => ''));
+ $table->column('float_value', 'float', array('precision' => 2, 'scale' => 1, 'default' => 0.0));
+ $table->column('decimal_value', 'decimal', array('precision' => 2, 'scale' => 1, 'default' => 0.0));
+ $table->column('datetime_value', 'datetime', array('default' => '0000-00-00 00:00:00'));
+ $table->column('date_value', 'date', array('default' => '0000-00-00'));
+ $table->column('time_value', 'time', array('default' => '00:00:00'));
+ $table->column('blob_value', 'binary', array('null' => false, 'default' => ''));
+ $table->column('boolean_value', 'boolean', array('default' => false));
+ $table->column('email_value', 'string', array('limit' => 255, 'default' => ''));
+ $table->end();
+ $this->_conn->addIndex('unit_tests', 'string_value', array('name' => 'string_value'));
+ $this->_conn->addIndex('unit_tests', 'integer_value', array('name' => 'integer_value', 'unique' => true));
+ $this->_conn->addIndex('unit_tests', array('integer_value', 'string_value'), array('name' => 'integer_string'));
+
+ // read sql file for statements
+ $statements = array();
+ $current_stmt = '';
+ $fp = fopen(dirname(__FILE__) . '/../../fixtures/unit_tests.sql', 'r');
+ while ($line = fgets($fp, 8192)) {
+ $line = rtrim(preg_replace('/^(.*)--.*$/s', '\1', $line));
+ if (!$line) {
+ continue;
+ }
+
+ $current_stmt .= $line;
+
+ if (substr($line, -1) == ';') {
+ // leave off the ending ;
+ $statements[] = substr($current_stmt, 0, -1);
+ $current_stmt = '';
+ }
+ }
+
+ // run statements
+ foreach ($statements as $stmt) {
+ $this->_conn->execute($stmt);
+ }
+ }
+
+ protected function tearDown()
+ {
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ /*##########################################################################
+ # Connection
+ ##########################################################################*/
+
+ public function testConnect()
+ {
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+ public function testDisconnect()
+ {
+ $this->_conn->disconnect();
+ $this->assertFalse($this->_conn->isActive());
+
+ $this->_conn->connect();
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+ public function testReconnect()
+ {
+ $this->_conn->reconnect();
+ $this->assertTrue($this->_conn->isActive());
+ }
+
+
+ /*##########################################################################
+ # Accessor
+ ##########################################################################*/
+
+ public function testAdapterName()
+ {
+ $this->assertEquals('PDO_SQLite', $this->_conn->adapterName());
+ }
+
+ public function testSupportsMigrations()
+ {
+ $this->assertTrue($this->_conn->supportsMigrations());
+ }
+
+ public function testSupportsCountDistinct()
+ {
+ $version = $this->_conn->selectValue('SELECT sqlite_version(*)');
+ if ($version >= '3.2.6') {
+ $this->assertTrue($this->_conn->supportsCountDistinct());
+ } else {
+ $this->assertFalse($this->_conn->supportsCountDistinct());
+ }
+ }
+
+
+ /*##########################################################################
+ # Database Statements
+ ##########################################################################*/
+
+ public function testExecute()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->execute($sql);
+ $row = $result->fetch();
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelect()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->select($sql);
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectWithBoundParameters()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id=?";
+ $result = $this->_conn->select($sql, array(1));
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectWithBoundParametersQuotesString()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE string_value=?";
+ $result = $this->_conn->select($sql, array('name a'));
+ $this->assertType('Traversable', $result);
+ $this->assertGreaterThan(0, count($result));
+
+ foreach ($result as $row) break;
+ $this->assertType('array', $row);
+ $this->assertEquals(1, $row['id']);
+ }
+
+ public function testSelectAll()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectAll($sql);
+ $this->assertType('array', $result);
+ $this->assertGreaterThan(0, count($result));
+ $this->assertEquals(1, $result[0]['id']);
+ }
+
+ public function testSelectOne()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectOne($sql);
+ $this->assertArrayHasKey('id', $result);
+ $this->assertEquals(1, $result['id']);
+ }
+
+ public function testSelectValue()
+ {
+ $sql = "SELECT * FROM unit_tests WHERE id='1'";
+ $result = $this->_conn->selectValue($sql);
+ $this->assertEquals(1, $result);
+ }
+
+ public function testSelectValues()
+ {
+ $sql = "SELECT * FROM unit_tests";
+ $result = $this->_conn->selectValues($sql);
+ $this->assertEquals(array(1, 2, 3, 4, 5, 6), $result);
+ }
+
+ public function testInsert()
+ {
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $result = $this->_conn->insert($sql);
+
+ $this->assertEquals(7, $result);
+ }
+
+ public function testUpdate()
+ {
+ $sql = "UPDATE unit_tests SET integer_value=999 WHERE id IN (1)";
+ $result = $this->_conn->update($sql);
+
+ $this->assertEquals(1, $result);
+ }
+
+ public function testDelete()
+ {
+ $sql = "DELETE FROM unit_tests WHERE id IN (1,2)";
+ $result = $this->_conn->delete($sql);
+
+ $this->assertEquals(2, $result);
+ }
+
+ public function testTransactionStarted()
+ {
+ $this->assertFalse($this->_conn->transactionStarted());
+ $this->_conn->beginDbTransaction();
+
+ $this->assertTrue($this->_conn->transactionStarted());
+ $this->_conn->commitDbTransaction();
+
+ $this->assertFalse($this->_conn->transactionStarted());
+ }
+
+ public function testTransactionCommit()
+ {
+ $this->_conn->beginDbTransaction();
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $this->_conn->insert($sql);
+ $this->_conn->commitDbTransaction();
+
+ // make sure it inserted
+ $sql = "SELECT integer_value FROM unit_tests WHERE id='7'";
+ $this->assertEquals('999', $this->_conn->selectValue($sql));
+ }
+
+ public function testTransactionRollback()
+ {
+ $this->_conn->beginDbTransaction();
+ $sql = "INSERT INTO unit_tests (id, integer_value) VALUES (7, 999)";
+ $this->_conn->insert($sql);
+ $this->_conn->rollbackDbTransaction();
+
+ // make sure it inserted
+ $sql = "SELECT integer_value FROM unit_tests WHERE id='7'";
+ $this->assertEquals(null, $this->_conn->selectValue($sql));
+ }
+
+ /*##########################################################################
+ # Quoting
+ ##########################################################################*/
+
+ public function testQuoteNull()
+ {
+ $this->assertEquals('NULL', $this->_conn->quote(null));
+ }
+
+ public function testQuoteTrue()
+ {
+ $this->assertEquals("'t'", $this->_conn->quote(true));
+ }
+
+ public function testQuoteFalse()
+ {
+ $this->assertEquals("'f'", $this->_conn->quote(false));
+ }
+
+ public function testQuoteString()
+ {
+ $this->assertEquals("'my string'", $this->_conn->quote('my string'));
+ }
+
+ public function testQuoteDirtyString()
+ {
+ $this->assertEquals("'derek''s string'", $this->_conn->quote('derek\'s string'));
+ }
+
+ public function testQuoteColumnName()
+ {
+ $col = new Horde_Db_Adapter_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 testTableAliasFor()
+ {
+ $alias = $this->_conn->tableAliasFor('my_table_name');
+ $this->assertEquals('my_table_name', $alias);
+ }
+
+ public function testTables()
+ {
+ $tables = $this->_conn->tables();
+ $this->assertTrue(count($tables) > 0);
+ $this->assertContains('unit_tests', $tables);
+ }
+
+ public function testIndexes()
+ {
+ $indexes = $this->_conn->indexes('unit_tests');
+ $this->assertEquals(3, count($indexes));
+
+ // sort indexes alphabetically
+ usort($indexes, create_function('$a, $b', 'return strcmp($a->name, $b->name);'));
+
+ // multi-column index
+ $col = array('integer_value', 'string_value');
+ $this->assertEquals('unit_tests', $indexes[0]->table);
+ $this->assertEquals('integer_string', $indexes[0]->name);
+ $this->assertEquals(false, $indexes[0]->unique);
+ $this->assertEquals($col, $indexes[0]->columns);
+
+ // unique index
+ $col = array('integer_value');
+ $this->assertEquals('unit_tests', $indexes[1]->table);
+ $this->assertEquals('integer_value', $indexes[1]->name);
+ $this->assertEquals(true, $indexes[1]->unique);
+ $this->assertEquals($col, $indexes[1]->columns);
+
+ // normal index
+ $col = array('string_value');
+ $this->assertEquals('unit_tests', $indexes[2]->table);
+ $this->assertEquals('string_value', $indexes[2]->name);
+ $this->assertEquals(false, $indexes[2]->unique);
+ $this->assertEquals($col, $indexes[2]->columns);
+ }
+
+ public function testColumns()
+ {
+ $columns = $this->_conn->columns('unit_tests');
+ $this->assertEquals(12, count($columns));
+
+ $col = $columns[0];
+ $this->assertEquals('id', $col->getName());
+ $this->assertEquals('integer', $col->getType());
+ $this->assertEquals(false, $col->isNull());
+ $this->assertEquals(null, $col->getLimit());
+ $this->assertEquals('', $col->getDefault());
+ $this->assertEquals('INTEGER', $col->getSqlType());
+ $this->assertEquals(false, $col->isText());
+ $this->assertEquals(true, $col->isNumber());
+ }
+
+ public function testCreateTable()
+ {
+ $this->_createTestTable('sports');
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testCreateTableNoPk()
+ {
+ $this->_createTestTable('sports', array('primaryKey' => false));
+
+ try {
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertNull($this->_conn->selectValue($sql));
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Expected exception for no pk");
+ }
+
+ public function testCreateTableForce()
+ {
+ $this->_createTestTable('sports');
+ $this->_createTestTable('sports', array('force' => true));
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testCreateTableTemporary()
+ {
+ $this->_createTestTable('sports', array('temporary' => true));
+
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->assertEquals(1, $this->_conn->selectValue($sql));
+ }
+
+ public function testRenameTable()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->renameTable('sports', 'my_sports');
+
+ $sql = "SELECT id FROM my_sports WHERE id = 1";
+ $this->assertEquals("1", $this->_conn->selectValue($sql));
+ }
+
+ public function testDropTable()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->dropTable('sports');
+
+ try {
+ $sql = "SELECT id FROM sports WHERE id = 1";
+ $this->_conn->execute($sql);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Table exists where it shouldn't have");
+ }
+
+ public function testAddColumn()
+ {
+ $this->_createTestTable('sports');
+ $this->_conn->addColumn('sports', 'modified_at', 'date');
+ $this->_conn->update("UPDATE sports SET modified_at = '2007-01-01'");
+
+ $sql = "SELECT modified_at FROM sports WHERE id = 1";
+ $this->assertEquals("2007-01-01", $this->_conn->selectValue($sql));
+ }
+
+ public function testRemoveColumn()
+ {
+ $this->_createTestTable('sports');
+ $sql = "SELECT name FROM sports WHERE id = 1";
+ $this->assertEquals("mlb", $this->_conn->selectValue($sql));
+
+ try {
+ $this->_conn->removeColumn('sports', 'name');
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail('SQLite#removeColumn is not yet implemented; should throw Horde_Db_Exception');
+
+ try {
+ $sql = "SELECT name FROM sports WHERE id = 1";
+ $this->_conn->execute($sql);
+ } catch (Exception $e) {
+ return;
+ }
+ $this->fail("Column exists where it shouldn't have");
+ }
+
+ public function testChangeColumnDefault()
+ {
+ $this->_createTestTable('sports');
+ $beforeChange = $this->_getColumn('sports', 'name');
+ $this->assertEquals('', $beforeChange->getDefault());
+
+ try {
+ $this->_conn->changeColumnDefault('sports', 'name', 'test');
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail('SQLite#changeColumnDefault is not yet implemented; should throw Horde_Db_Exception');
+
+ $afterChange = $this->_getColumn('sports', 'name');
+ $this->assertEquals('test', $afterChange->getDefault());
+ }
+
+ public function testChangeColumnType()
+ {
+ $this->_createTestTable('sports');
+ $beforeChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('boolean', $beforeChange->getSqlType());
+
+ try {
+ $this->_conn->changeColumn('sports', 'is_college', 'string');
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail('SQLite#changeColumn is not yet implemented; should throw Horde_Db_Exception');
+
+ $afterChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('varchar(255)', $afterChange->getSqlType());
+ }
+
+ public function testChangeColumnLimit()
+ {
+ $this->_createTestTable('sports');
+ $beforeChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('boolean', $beforeChange->getSqlType());
+
+ try {
+ $this->_conn->changeColumn('sports', 'is_college', 'string',
+ array('limit' => '40'));
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail('SQLite#changeColumn is not yet implemented; should throw Horde_Db_Exception');
+
+ $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());
+
+ try {
+ $this->_conn->changeColumn('sports', 'is_college', 'decimal',
+ array('precision' => '5', 'scale' => '2'));
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail('SQLite#changeColumn is not yet implemented; should throw Horde_Db_Exception');
+
+ $afterChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('decimal(5,2)', $afterChange->getSqlType());
+ }
+
+ public function testRenameColumn()
+ {
+ $this->_createTestTable('sports');
+
+ $beforeChange = $this->_getColumn('sports', 'is_college');
+ $this->assertEquals('boolean', $beforeChange->getSqlType());
+
+ try {
+ $this->_conn->renameColumn('sports', 'is_college', 'is_renamed');
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail('SQLite#renameColumn is not yet implemented; should throw Horde_Db_Exception');
+
+ $afterChange = $this->_getColumn('sports', 'is_renamed');
+ $this->assertEquals('boolean', $afterChange->getSqlType());
+ }
+
+ public function testAddIndexDefault()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college');
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ }
+
+ public function testAddIndexMultiColumn()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', array('name', 'is_college'));
+
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNotNull($index);
+ }
+
+ public function testAddIndexUnique()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college', array('unique' => true));
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ $this->assertTrue($index->unique);
+ }
+
+ public function testAddIndexName()
+ {
+ $this->_createTestTable('sports');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+
+ $this->_conn->addIndex('sports', 'is_college', array('name' => 'test'));
+
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+ $this->assertEquals('test', $index->name);
+ }
+
+ public function testRemoveIndexSingleColumn()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', 'is_college');
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('column' => 'is_college'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+ }
+
+ public function testRemoveIndexMultiColumn()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', array('name', 'is_college'));
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('column' => array('name', 'is_college')));
+ $index = $this->_getIndex('sports', array('name', 'is_college'));
+ $this->assertNull($index);
+ }
+
+ public function testRemoveIndexByName()
+ {
+ $this->_createTestTable('sports');
+
+ // add the index
+ $this->_conn->addIndex('sports', 'is_college', array('name' => 'test'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNotNull($index);
+
+ // remove it again
+ $this->_conn->removeIndex('sports', array('name' => 'test'));
+ $index = $this->_getIndex('sports', 'is_college');
+ $this->assertNull($index);
+ }
+
+ public function testIndexNameInvalid()
+ {
+ try {
+ $name = $this->_conn->indexName('sports');
+ } catch (Horde_Db_Exception $e) {
+ return;
+ }
+ $this->fail("Adding an index with crappy options worked where it shouldn't have");
+ }
+
+ public function testIndexNameBySingleColumn()
+ {
+ $name = $this->_conn->indexName('sports', array('column' => 'is_college'));
+ $this->assertEquals('index_sports_on_is_college', $name);
+ }
+
+ public function testIndexNameByMultiColumn()
+ {
+ $name = $this->_conn->indexName('sports', array('column' =>
+ array('name', 'is_college')));
+ $this->assertEquals('index_sports_on_name_and_is_college', $name);
+ }
+
+ public function testIndexNameByName()
+ {
+ $name = $this->_conn->indexName('sports', array('name' => 'test'));
+ $this->assertEquals('test', $name);
+ }
+
+ public function testStructureDump()
+ {
+ $this->_createTestTable('sports');
+
+ // single table
+ $structure = $this->_conn->structureDump('sports');
+
+ $expected = "CREATE TABLE \"sports\" (\n".
+ " \"id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \n".
+ " \"name\" varchar(255), \n".
+ " \"is_college\" boolean\n".
+ ")";
+
+ $this->assertContains($expected, $structure);
+
+ // entire structure
+ $structure = $this->_conn->structureDump();
+
+ // contains, but doesn't match only sports table
+ $this->assertContains($expected, $structure);
+ $this->assertNotEquals($expected, $structure);
+ }
+
+ public function testInitializeSchemaInformation()
+ {
+ $this->_conn->initializeSchemaInformation();
+
+ $sql = "SELECT version FROM schema_info";
+ $this->assertEquals(0, $this->_conn->selectValue($sql));
+ }
+
+ public function testTypeToSqlTypePrimaryKey()
+ {
+ $result = $this->_conn->typeToSql('primaryKey');
+ $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', '11');
+ $this->assertEquals('int(11)', $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 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);
+ }
+
+ /*##########################################################################
+ # Test Cached table descriptions
+ ##########################################################################*/
+
+ public function testCachedTableDescription()
+ {
+ // remove any current cache.
+ $this->_cache->set('tables/cache_table', '');
+ $this->assertEquals('', $this->_cache->get('tables/cache_table'));
+
+ $this->_createTestTable('cache_table');
+ $cols = $this->_conn->columns('cache_table');
+
+ $this->assertNotEquals('', $this->_cache->get('tables/cache_table'));
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * Create table to perform tests on
+ */
+ protected function _createTestTable($name, $options=array())
+ {
+ $table = $this->_conn->createTable($name, $options);
+ $table->column('name', 'string');
+ $table->column('is_college', 'boolean');
+ $table->end();
+
+ try {
+ // make sure table was created
+ $sql = "INSERT INTO $name (id, name, is_college)
+ VALUES (1, 'mlb', 0)";
+ $this->_conn->insert($sql);
+ } catch (Exception $e) {}
+ }
+
+ /**
+ * 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;
+ }
+ }
+
+}
--- /dev/null
+/ColumnDefinitionTest.php/1.1/Fri Sep 19 03:59:03 2008//
+/ColumnTest.php/1.1/Fri Sep 19 03:59:03 2008//
+/TableDefinitionTest.php/1.1/Fri Sep 19 03:59:03 2008//
+D
--- /dev/null
+framework/Db/test/Horde/Db/Adapter/Postgresql
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Postgresql_ColumnDefinitionTest extends PHPUnit_Framework_TestCase
+{
+ protected function setUp()
+ {
+ list($this->_conn,) = $this->sharedFixture->getConnection();
+ }
+
+ protected function tearDown()
+ {
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ public function testConstruct()
+ {
+ $col = new Horde_Db_Adapter_Abstract_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_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string'
+ );
+ $this->assertEquals('"col_name" character varying(255)', $col->toSql());
+ }
+
+ public function testToSqlLimit()
+ {
+ $col = new Horde_Db_Adapter_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string', 40
+ );
+ $this->assertEquals('"col_name" character varying(40)', $col->toSql());
+
+ // set attribute instead
+ $col = new Horde_Db_Adapter_Abstract_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_Abstract_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_Abstract_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_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string', null, null, null, null, false
+ );
+ $this->assertEquals('"col_name" character varying(255) NOT NULL', $col->toSql());
+
+ // set attribute instead
+ $col = new Horde_Db_Adapter_Abstract_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_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string', null, null, null, 'test', null
+ );
+ $this->assertEquals('"col_name" character varying(255) DEFAULT \'test\'', $col->toSql());
+
+ // set attribute instead
+ $col = new Horde_Db_Adapter_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string'
+ );
+ $col->setDefault('test');
+ $this->assertEquals('"col_name" character varying(255) DEFAULT \'test\'', $col->toSql());
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Postgresql_ColumnTest extends PHPUnit_Framework_TestCase
+{
+ /*##########################################################################
+ # 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->isNull());
+ }
+
+ public function testSqlType()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)');
+ $this->assertEquals('character varying(255)', $col->getSqlType());
+ }
+
+ public function testIsText()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)');
+ $this->assertTrue($col->isText());
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'text');
+ $this->assertTrue($col->isText());
+
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'int(11)');
+ $this->assertFalse($col->isText());
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'float(11,1)');
+ $this->assertFalse($col->isText());
+ }
+
+ public function testIsNumber()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)');
+ $this->assertFalse($col->isNumber());
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'text');
+ $this->assertFalse($col->isNumber());
+
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'int(11)');
+ $this->assertTrue($col->isNumber());
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'float(11,1)');
+ $this->assertTrue($col->isNumber());
+ }
+
+
+ /*##########################################################################
+ # Types
+ ##########################################################################*/
+
+ public function testTypeInteger()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'int(11)');
+ $this->assertEquals('integer', $col->getType());
+ }
+
+ public function testTypeFloat()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'float(11,1)');
+ $this->assertEquals('float', $col->getType());
+ }
+
+ public function testTypeDecimalPrecisionNone()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'decimal(11,0)');
+ $this->assertEquals('integer', $col->getType());
+ }
+
+ public function testTypeDecimal()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'decimal(11,1)');
+ $this->assertEquals('decimal', $col->getType());
+ }
+
+ public function testTypeDatetime()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'datetime');
+ $this->assertEquals('datetime', $col->getType());
+ }
+
+ public function testTypeTimestamp()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'CURRENT_TIMESTAMP', 'timestamp');
+ $this->assertEquals('timestamp', $col->getType());
+ }
+
+ public function testTypeTime()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'time');
+ $this->assertEquals('time', $col->getType());
+ }
+
+ public function testTypeDate()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'date');
+ $this->assertEquals('date', $col->getType());
+ }
+
+ public function testTypeText()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'text');
+ $this->assertEquals('text', $col->getType());
+ }
+
+ public function testTypeBinary()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('age', 'NULL', 'blob(255)');
+ $this->assertEquals('binary', $col->getType());
+ }
+
+ public function testTypeString()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('name', 'NULL', 'character varying(255)');
+ $this->assertEquals('string', $col->getType());
+ }
+
+ /*##########################################################################
+ # Primary
+ ##########################################################################*/
+
+ public function testPrimary()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('id', 'NULL', 'int(11)');
+ $this->assertFalse($col->isPrimary());
+
+ $col->setPrimary(true);
+ $this->assertTrue($col->isPrimary());
+ }
+
+ /*##########################################################################
+ # Extract Limit
+ ##########################################################################*/
+
+ public function testExtractLimitInt()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'int(11)');
+ $this->assertEquals(11, $col->getLimit());
+ }
+
+ public function testExtractLimitVarchar()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'character varying(255)');
+ $this->assertEquals(255, $col->getLimit());
+ }
+
+ public function testExtractLimitDecimal()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'decimal(11,1)');
+ $this->assertEquals('11', $col->getLimit());
+ }
+
+ public function testExtractLimitText()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'text');
+ $this->assertEquals(null, $col->getLimit());
+ }
+
+ public function testExtractLimitNone()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL');
+ $this->assertEquals(null, $col->getLimit());
+ }
+
+ /*##########################################################################
+ # Extract Precision/Scale
+ ##########################################################################*/
+
+ public function testExtractPrecisionScale()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('test', 'NULL', 'decimal(12,1)');
+ $this->assertEquals('12', $col->precision());
+ $this->assertEquals('1', $col->scale());
+ }
+
+
+ /*##########################################################################
+ # Type Cast Values
+ ##########################################################################*/
+
+ public function testTypeCastInteger()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('name', '1', 'int(11)', false);
+ $this->assertEquals(1, $col->getDefault());
+ }
+
+ public function testTypeCastFloat()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('version', '1.0', 'float(11,1)', false);
+ $this->assertEquals(1.0, $col->getDefault());
+ }
+
+ public function testTypeCastString()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('name', "'n/a'::character varying", 'character varying(255)', false);
+ $this->assertEquals('n/a', $col->getDefault());
+ }
+
+ public function testTypeCastBooleanFalse()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('is_active', '0', 'boolean', false);
+ $this->assertSame(false, $col->getDefault());
+ }
+
+ public function testTypeCastBooleanTrue()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('is_active', '1', 'boolean', false);
+ $this->assertSame(true, $col->getDefault());
+ }
+
+ /*##########################################################################
+ # Column Types
+ ##########################################################################*/
+
+ /*@TODO tests for PostgreSQL-specific column types */
+
+
+ /*##########################################################################
+ # Defaults
+ ##########################################################################*/
+
+ public function testDefaultDatetime()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'datetime');
+ $this->assertEquals(null, $col->getDefault());
+ }
+
+ public function testDefaultInteger()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'int(11)');
+ $this->assertEquals(null, $col->getDefault());
+ }
+
+ public function testDefaultString()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'character varying(255)');
+ $this->assertEquals('', $col->getDefault());
+ }
+
+ public function testDefaultText()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'text');
+ $this->assertEquals('', $col->getDefault());
+ }
+
+ public function testDefaultBinary()
+ {
+ $col = new Horde_Db_Adapter_Postgresql_Column('name', '', 'blob(255)');
+ $this->assertEquals('', $col->getDefault());
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Postgresql_TableDefinitionTest extends PHPUnit_Framework_TestCase
+{
+ protected function setUp()
+ {
+ list($this->_conn,) = $this->sharedFixture->getConnection();
+ }
+
+ protected function tearDown()
+ {
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ /*##########################################################################
+ # Public methods
+ ##########################################################################*/
+
+ public function testConstruct()
+ {
+ }
+
+ public function testName()
+ {
+ }
+
+ public function testGetOptions()
+ {
+ }
+
+ public function testPrimaryKey()
+ {
+ }
+
+ public function testColumn()
+ {
+ }
+
+ public function testToSql()
+ {
+ }
+
+ /*##########################################################################
+ # Array Access
+ ##########################################################################*/
+
+ public function testOffsetExists()
+ {
+ }
+
+ public function testOffsetGet()
+ {
+ }
+
+ public function testOffsetSet()
+ {
+ }
+
+ public function testOffsetUnset()
+ {
+ }
+
+}
--- /dev/null
+/ColumnDefinitionTest.php/1.1/Fri Sep 19 03:59:03 2008//
+/ColumnTest.php/1.1/Fri Sep 19 03:59:03 2008//
+/TableDefinitionTest.php/1.1/Fri Sep 19 03:59:03 2008//
+D
--- /dev/null
+framework/Db/test/Horde/Db/Adapter/Sqlite
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Sqlite_ColumnDefinitionTest extends PHPUnit_Framework_TestCase
+{
+ protected function setUp()
+ {
+ list($this->_conn,) = $this->sharedFixture->getConnection();
+ }
+
+ protected function tearDown()
+ {
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ public function testConstruct()
+ {
+ $col = new Horde_Db_Adapter_Abstract_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_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string'
+ );
+ $this->assertEquals('"col_name" varchar(255)', $col->toSql());
+ }
+
+ public function testToSqlLimit()
+ {
+ $col = new Horde_Db_Adapter_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string', 40
+ );
+ $this->assertEquals('"col_name" varchar(40)', $col->toSql());
+
+ // set attribute instead
+ $col = new Horde_Db_Adapter_Abstract_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_Abstract_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_Abstract_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_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string', null, null, null, null, false
+ );
+ $this->assertEquals('"col_name" varchar(255) NOT NULL', $col->toSql());
+
+ // set attribute instead
+ $col = new Horde_Db_Adapter_Abstract_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_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string', null, null, null, 'test', null
+ );
+ $this->assertEquals('"col_name" varchar(255) DEFAULT \'test\'', $col->toSql());
+
+ // set attribute instead
+ $col = new Horde_Db_Adapter_Abstract_ColumnDefinition(
+ $this->_conn, 'col_name', 'string'
+ );
+ $col->setDefault('test');
+ $this->assertEquals('"col_name" varchar(255) DEFAULT \'test\'', $col->toSql());
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Sqlite_ColumnTest extends PHPUnit_Framework_TestCase
+{
+ /*##########################################################################
+ # Construction
+ ##########################################################################*/
+
+ public function testDefaultNull()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)');
+ $this->assertEquals(true, $col->isNull());
+ }
+
+ public function testNotNull()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)', false);
+ $this->assertEquals(false, $col->isNull());
+ }
+
+ public function testName()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)');
+ $this->assertEquals('name', $col->isNull());
+ }
+
+ public function testSqlType()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)');
+ $this->assertEquals('varchar(255)', $col->getSqlType());
+ }
+
+ public function testIsText()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'varchar(255)');
+ $this->assertTrue($col->isText());
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'text');
+ $this->assertTrue($col->isText());
+
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'int(11)');
+ $this->assertFalse($col->isText());
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'float(11,1)');
+ $this->assertFalse($col->isText());
+ }
+
+ public function testIsNumber()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'varchar(255)');
+ $this->assertFalse($col->isNumber());
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'text');
+ $this->assertFalse($col->isNumber());
+
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'int(11)');
+ $this->assertTrue($col->isNumber());
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'float(11,1)');
+ $this->assertTrue($col->isNumber());
+ }
+
+
+ /*##########################################################################
+ # Types
+ ##########################################################################*/
+
+ public function testTypeInteger()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'int(11)');
+ $this->assertEquals('integer', $col->getType());
+ }
+
+ public function testTypeFloat()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'float(11,1)');
+ $this->assertEquals('float', $col->getType());
+ }
+
+ public function testTypeDecimalPrecisionNone()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'decimal(11,0)');
+ $this->assertEquals('integer', $col->getType());
+ }
+
+ public function testTypeDecimal()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'decimal(11,1)');
+ $this->assertEquals('decimal', $col->getType());
+ }
+
+ public function testTypeDatetime()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'datetime');
+ $this->assertEquals('datetime', $col->getType());
+ }
+
+ public function testTypeTimestamp()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'CURRENT_TIMESTAMP', 'timestamp');
+ $this->assertEquals('timestamp', $col->getType());
+ }
+
+ public function testTypeTime()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'time');
+ $this->assertEquals('time', $col->getType());
+ }
+
+ public function testTypeDate()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'date');
+ $this->assertEquals('date', $col->getType());
+ }
+
+ public function testTypeText()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'text');
+ $this->assertEquals('text', $col->getType());
+ }
+
+ public function testTypeBinary()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('age', 'NULL', 'blob(255)');
+ $this->assertEquals('binary', $col->getType());
+ }
+
+ public function testTypeString()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', 'NULL', 'varchar(255)');
+ $this->assertEquals('string', $col->getType());
+ }
+
+ /*##########################################################################
+ # Primary
+ ##########################################################################*/
+
+ public function testPrimary()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('id', 'NULL', 'int(11)');
+ $this->assertFalse($col->isPrimary());
+
+ $col->setPrimary(true);
+ $this->assertTrue($col->isPrimary());
+ }
+
+ /*##########################################################################
+ # Extract Limit
+ ##########################################################################*/
+
+ public function testExtractLimitInt()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'int(11)');
+ $this->assertEquals(11, $col->getLimit());
+ }
+
+ public function testExtractLimitVarchar()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'varchar(255)');
+ $this->assertEquals(255, $col->getLimit());
+ }
+
+ public function testExtractLimitDecimal()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'decimal(11,1)');
+ $this->assertEquals('11', $col->getLimit());
+ }
+
+ public function testExtractLimitText()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'text');
+ $this->assertEquals(null, $col->getLimit());
+ }
+
+ public function testExtractLimitNone()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL');
+ $this->assertEquals(null, $col->getLimit());
+ }
+
+ /*##########################################################################
+ # Extract Precision/Scale
+ ##########################################################################*/
+
+ public function testExtractPrecisionScale()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('test', 'NULL', 'decimal(12,1)');
+ $this->assertEquals('12', $col->precision());
+ $this->assertEquals('1', $col->scale());
+ }
+
+
+ /*##########################################################################
+ # Type Cast Values
+ ##########################################################################*/
+
+ public function testTypeCastInteger()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', '1', 'int(11)', false);
+ $this->assertEquals(1, $col->getDefault());
+ }
+
+ public function testTypeCastFloat()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('version', '1.0', 'float(11,1)', false);
+ $this->assertEquals(1.0, $col->getDefault());
+ }
+
+ public function testTypeCastString()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', 'n/a', 'varchar(255)', false);
+ $this->assertEquals('n/a', $col->getDefault());
+ }
+
+ public function testTypeCastBooleanFalse()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('is_active', 'f', 'boolean', false);
+ $this->assertSame(false, $col->getDefault());
+ }
+
+ public function testTypeCastBooleanTrue()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('is_active', 't', 'boolean', false);
+ $this->assertSame(true, $col->getDefault());
+ }
+
+
+ /*##########################################################################
+ # Defaults
+ ##########################################################################*/
+
+ public function testDefaultDatetime()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'datetime');
+ $this->assertEquals(null, $col->getDefault());
+ }
+
+ public function testDefaultInteger()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'int(11)');
+ $this->assertEquals(null, $col->getDefault());
+ }
+
+ public function testDefaultString()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'varchar(255)');
+ $this->assertEquals('', $col->getDefault());
+ }
+
+ public function testDefaultText()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'text');
+ $this->assertEquals('', $col->getDefault());
+ }
+
+ public function testDefaultBinary()
+ {
+ $col = new Horde_Db_Adapter_Sqlite_Column('name', '', 'blob(255)');
+ $this->assertEquals('', $col->getDefault());
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @group horde_db
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_Adapter_Sqlite_TableDefinitionTest extends PHPUnit_Framework_TestCase
+{
+ protected function setUp()
+ {
+ list($this->_conn,) = $this->sharedFixture->getConnection();
+ }
+
+ protected function tearDown()
+ {
+ // close connection
+ $this->_conn->disconnect();
+ }
+
+
+ /*##########################################################################
+ # Public methods
+ ##########################################################################*/
+
+ public function testConstruct()
+ {
+ }
+
+ public function testName()
+ {
+ }
+
+ public function testGetOptions()
+ {
+ }
+
+ public function testPrimaryKey()
+ {
+ }
+
+ public function testColumn()
+ {
+ }
+
+ public function testToSql()
+ {
+ }
+
+ /*##########################################################################
+ # Array Access
+ ##########################################################################*/
+
+ public function testOffsetExists()
+ {
+ }
+
+ public function testOffsetGet()
+ {
+ }
+
+ public function testOffsetSet()
+ {
+ }
+
+ public function testOffsetUnset()
+ {
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008 The Horde Project (http://www.horde.org/)
+ *
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+ define('PHPUnit_MAIN_METHOD', 'Horde_Db_AllTests::main');
+}
+
+require_once 'PHPUnit/Framework/TestSuite.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+require_once dirname(__FILE__).'/Adapter/MissingTest.php';
+
+/**
+ * @author Mike Naberezny <mike@maintainable.com>
+ * @author Derek DeVries <derek@maintainable.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @license http://opensource.org/licenses/bsd-license.php
+ * @category Horde
+ * @package Horde_Db
+ * @subpackage UnitTests
+ */
+class Horde_Db_AllTests {
+
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ public static function suite()
+ {
+ // Catch strict standards
+ error_reporting(E_ALL | E_STRICT);
+
+ // Ensure a default timezone is set.
+ date_default_timezone_set('America/New_York');
+
+ // Set up autoload
+ set_include_path(dirname(dirname(dirname(dirname(__FILE__)))) . DIRECTORY_SEPARATOR . 'lib' . PATH_SEPARATOR . get_include_path());
+ if (!spl_autoload_functions()) {
+ spl_autoload_register(create_function('$class', '$filename = str_replace(array(\'::\', \'_\'), \'/\', $class); @include_once "$filename.php";'));
+ }
+
+ // Build the suite
+ $suite = new PHPUnit_Framework_TestSuite('Horde Framework - Horde_Db');
+
+ $basedir = dirname(__FILE__);
+ $baseregexp = preg_quote($basedir . DIRECTORY_SEPARATOR, '/');
+
+ foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($basedir)) as $file) {
+ if ($file->isFile() && preg_match('/Suite.php$/', $file->getFilename())) {
+ $pathname = $file->getPathname();
+ require $pathname;
+
+ $class = str_replace(DIRECTORY_SEPARATOR, '_',
+ preg_replace("/^$baseregexp(.*)\.php/", '\\1', $pathname));
+ $suite->addTestSuite('Horde_Db_'.$class);
+ }
+ }
+
+ return $suite;
+ }
+
+}
+
+if (PHPUnit_MAIN_METHOD == 'Horde_Db_AllTests::main') {
+ Horde_Db_AllTests::main();
+}
--- /dev/null
+D/fixtures////
+D/Adapter////
+/AllTests.php/1.1/Fri Sep 19 03:59:03 2008//
--- /dev/null
+framework/Db/test/Horde/Db
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+/unit_tests.sql/1.1/Fri Sep 19 03:59:03 2008//
+D
--- /dev/null
+framework/Db/test/Horde/Db/fixtures
--- /dev/null
+chuck@cvs.horde.org:/repository
--- /dev/null
+
+Bug:
+Submitted by:
+Merge after:
+CVS: ----------------------------------------------------------------------
+CVS: Bug: Fill this in if a listed bug is affected by the change.
+CVS: Submitted by: Fill this in if someone else sent in the change.
+CVS: Merge after: N [day[s]|week[s]|month[s]] (days assumed by default)
--- /dev/null
+--
+-- Copyright 2007 Maintainable Software, LLC
+-- Copyright 2008 The Horde Project (http://www.horde.org/)
+--
+-- @author Mike Naberezny <mike@maintainable.com>
+-- @author Derek DeVries <derek@maintainable.com>
+-- @author Chuck Hagenbuch <chuck@horde.org>
+-- @license http://opensource.org/licenses/bsd-license.php
+-- @category Horde
+-- @package Horde_Db
+-- @subpackage UnitTests
+--
+INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('1', '1', 'name a', 'string a', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '1', 'foo@example.com');
+INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('2', '2', 'name b', 'string b', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '0', 'foo@example.com');
+INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('3', '3', 'name c', 'string a', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '1', 'foo@example.com');
+INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('4', '4', 'name d', 'string b', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '0', 'foo@example.com');
+INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('5', '5', 'name e', 'string b', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some blob data', '1', 'foo@example.com');
+INSERT INTO unit_tests (id, integer_value, string_value, text_value, float_value, decimal_value, datetime_value, date_value, time_value, blob_value, boolean_value, email_value) VALUES ('6', '6', 'name f', 'string b', '1.2', '1.2', '2005-12-23 12:34:23', '2005-12-23', '12:34:23', 'some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data some clob data', '0', 'foo@example.com');