--- /dev/null
- . (empty($runtime) ? '' : " ($runtime ms)");
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008-2009 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_Base
+{
+ /**
+ * 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_Base_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;
+ }
+
+ // Default to UTF-8
+ if (!isset($config['charset'])) {
+ $config['charset'] = 'UTF-8';
+ }
+
+ $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 factory
+ ##########################################################################*/
+
+ /**
+ * Delegate calls to the schema object.
+ *
+ * @param string $method
+ * @param array $args
+ */
+ public function componentFactory($component, $args)
+ {
+ $class = str_replace('_Schema', '', $this->_schemaClass) . '_' . $component;
+ if (class_exists($class)) {
+ $class = new ReflectionClass($class);
+ } else {
+ $class = new ReflectionClass('Horde_Db_Adapter_Base_' . $component);
+ }
+
+ return $class->newInstanceArgs($args);
+ }
+
+
+ /*##########################################################################
+ # 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 'Base';
+ }
+
+ /**
+ * Does this adapter support migrations? Backend specific, as the
+ * abstract adapter always returns +false+.
+ *
+ * @return boolean
+ */
+ public function supportsMigrations()
+ {
+ return false;
+ }
+
+ /**
+ * Does this adapter support using DISTINCT within COUNT? This is +true+
+ * for all adapters except sqlite.
+ *
+ * @return boolean
+ */
+ public function supportsCountDistinct()
+ {
+ return true;
+ }
+
+ /**
+ * Should primary key values be selected from their corresponding
+ * sequence before the insert statement? If true, next_sequence_value
+ * is called before each insert to set the record's primary key.
+ * This is false for all adapters but Firebird.
+ */
+ 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 ? next($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 ? next($result) : null;
+ }
+
+ /**
+ * Returns an array of the values of the first column in a select:
+ * selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3]
+ *
+ * @param string $sql
+ * @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);
+ $values = array();
+ foreach ($result as $row) {
+ $values[] = next($row);
+ }
+ return $values;
+ }
+
+ /**
+ * Returns an array where the keys are the first column of a select, and the
+ * values are the second column:
+ *
+ * selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler']
+ *
+ * @param string $sql
+ * @param mixed $arg1 Either an array of bound parameters or a query name.
+ * @param string $arg2 If $arg1 contains bound parameters, the query name.
+ */
+ public function selectAssoc($sql, $arg1 = null, $arg2 = null)
+ {
+ $result = $this->selectAll($sql, $arg1, $arg2);
+ $values = array();
+ foreach ($result as $row) {
+ $values[next($row)] = next($row);
+ }
+ 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();
+
+ 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) ? '' : sprintf(" (%.4fs)", $runtime));
+ $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
- $this->_type = $this->_simplifiedType($sqlType);
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008-2009 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_Base_Column
+{
+ protected $_name;
+ protected $_type;
+ protected $_null;
+ 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->_type = $this->_simplifiedType($sqlType);
++ $this->_isText = $this->_type == 'text' || $this->_type == 'string';
++ $this->_isNumber = $this->_type == 'float' || $this->_type == 'integer' || $this->_type == 'decimal';
++
+ $this->_limit = $this->_extractLimit($sqlType);
+ $this->_precision = $this->_extractPrecision($sqlType);
+ $this->_scale = $this->_extractScale($sqlType);
-
- $this->_isText = $this->_type == 'text' || $this->_type == 'string';
- $this->_isNumber = $this->_type == 'float' || $this->_type == 'integer' || $this->_type == 'decimal';
+ $this->_default = $this->extractDefault($default);
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isText()
+ {
+ return $this->_isText;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isNumber()
+ {
+ return $this->_isNumber;
+ }
+
+ /**
+ * Casts value (which is a String) to an appropriate instance.
+ */
+ public function typeCast($value)
+ {
+ if ($value === null) return null;
+
+ switch ($this->_type) {
+ case 'string':
+ case 'text':
+ return $value;
+ case 'integer':
+ return strlen($value) ? (int)$value : null;
+ case 'float':
+ return strlen($value) ? (float)$value : null;
+ case 'decimal':
+ return $this->valueToDecimal($value);
+ case 'datetime':
+ case 'timestamp':
+ return $this->stringToTime($value);
+ case 'time':
+ return $this->stringToDummyTime($value);
+ case 'date':
+ return $this->stringToDate($value);
+ case 'binary':
+ return $this->binaryToString($value);
+ case 'boolean':
+ return $this->valueToBoolean($value);
+ default:
+ return $value;
+ }
+ }
+
+ public function extractDefault($default)
+ {
+ return $this->typeCast($default);
+ }
+
+
+ /*##########################################################################
+ # Accessor
+ ##########################################################################*/
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDefault()
+ {
+ return $this->_default;
+ }
+
+ /**
+ * @return string
+ */
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ /**
+ * @return int
+ */
+ public function getLimit()
+ {
+ return $this->_limit;
+ }
+
+ /**
+ * @return int
+ */
+ public function precision()
+ {
+ return $this->_precision;
+ }
+
+ /**
+ * @return int
+ */
+ public function scale()
+ {
+ return $this->_scale;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isNull()
+ {
+ return $this->_null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getSqlType()
+ {
+ return $this->_sqlType;
+ }
+
+
+ /*##########################################################################
+ # 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;
+ }
+
+ /**
+ * @param string $string
+ * @return Horde_Date
+ */
+ public function stringToDate($string)
+ {
+ if (empty($string) ||
+ // preserve '0000-00-00' (http://bugs.php.net/bug.php?id=45647)
+ preg_replace('/[^\d]/', '', $string) == 0) {
+ return null;
+ }
+
+ $d = new Horde_Date($string);
+ $d->setDefaultFormat('Y-m-d');
+
+ return $d;
+ }
+
+ /**
+ * @param string $string
+ * @return Horde_Date
+ */
+ public function stringToTime($string)
+ {
+ if (empty($string) ||
+ // preserve '0000-00-00 00:00:00' (http://bugs.php.net/bug.php?id=45647)
+ preg_replace('/[^\d]/', '', $string) == 0) {
+ return null;
+ }
+
+ return new Horde_Date($string);
+ }
+
+ /**
+ * @TODO Return a Horde_Date object instead?
+ *
+ * @param string $string
+ * @return Horde_Date
+ */
+ public function stringToDummyTime($value)
+ {
+ if (empty($string)) {
+ return null;
+ }
+ return $this->stringToTime('2000-01-01 ' . $string);
+ }
+
+ /**
+ * @param mixed $value
+ * @return boolean
+ */
+ public function valueToBoolean($value)
+ {
+ if ($value === true || $value === false) {
+ return $value;
+ }
+
+ $value = strtolower($value);
+ return $value == 'true' || $value == 't' || $value == '1';
+ }
+
+ /**
+ * @param mixed $value
+ * @return decimal
+ */
+ public function valueToDecimal($value)
+ {
+ return (float)$value;
+ }
+
+
+ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * @param string $sqlType
+ * @return int
+ */
+ protected function _extractLimit($sqlType)
+ {
+ if (preg_match("/\((.*)\)/", $sqlType, $matches)) {
+ return (int)$matches[1];
+ }
+ return null;
+ }
+
+ /**
+ * @param string $sqlType
+ * @return int
+ */
+ protected function _extractPrecision($sqlType)
+ {
+ if (preg_match("/^(numeric|decimal|number)\((\d+)(,\d+)?\)/i", $sqlType, $matches)) {
+ return (int)$matches[2];
+ }
+ return null;
+ }
+
+ /**
+ * @param string $sqlType
+ * @return int
+ */
+ protected function _extractScale($sqlType)
+ {
+ switch (true) {
+ case preg_match("/^(numeric|decimal|number)\((\d+)\)/i", $sqlType):
+ return 0;
+ case preg_match("/^(numeric|decimal|number)\((\d+)(,(\d+))\)/i",
+ $sqlType, $match):
+ return (int)$match[4];
+ }
+ }
+
+ /**
+ * @param string $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
- class Horde_Db_Adapter_Base_TableDefinition implements ArrayAccess
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2008-2009 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_Base_TableDefinition implements ArrayAccess, IteratorAggregate
+{
+ protected $_name = null;
+ protected $_base = null;
+ protected $_options = null;
+ protected $_columns = null;
+
+ /**
+ * Class Constructor
+ *
+ * @param string $name
+ * @param Horde_Db_Adapter_Base_Schema $base
+ * @param array $options
+ */
+ public function __construct($name, $base, $options=array())
+ {
+ $this->_name = $name;
+ $this->_base = $base;
+ $this->_options = $options;
+ $this->_columns = array();
+ }
+
+ /**
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+ /**
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->_options;
+ }
+
++ /**v
+ * @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 = $this->_base->componentFactory('ColumnDefinition', array(
+ $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_Base_ColumnDefinition}
+ */
+ public function offsetGet($offset)
+ {
+ if (!$this->offsetExists($offset)) {
+ return null;
+ }
+
+ foreach ($this->_columns as $column) {
+ if ($column->getName() == $offset) {
+ return $column;
+ }
+ }
+ }
+
+ /**
+ * ArrayAccess: Set value for given offset
+ *
+ * @param int $offset
+ * @param mixed $value
+ */
+ public function offsetSet($offset, $value)
+ {
+ foreach ($this->_columns as $key=>$column) {
+ if ($column->getName() == $offset) {
+ $this->_columns[$key] = $value;
+ }
+ }
+ }
+
+ /**
+ * ArrayAccess: remove element
+ *
+ * @param int $offset
+ */
+ public function offsetUnset($offset)
+ {
+ foreach ($this->_columns as $key=>$column) {
+ if ($column->getName() == $offset) {
+ unset($this->_columns[$key]);
+ }
+ }
+ }
+
+
+ /*##########################################################################
++ # ArrayAccess
++ ##########################################################################*/
++
++ public function getIterator()
++ {
++ return new ArrayIterator($this->_columns);
++ }
++
++
++ /*##########################################################################
+ # Protected
+ ##########################################################################*/
+
+ /**
+ * Get the types
+ */
+ protected function _native()
+ {
+ return $this->_base->nativeDatabaseTypes();
+ }
+
+}