Abstract -> Base
authorJan Schneider <jan@horde.org>
Sun, 6 Sep 2009 10:23:17 +0000 (12:23 +0200)
committerJan Schneider <jan@horde.org>
Sun, 6 Sep 2009 10:23:17 +0000 (12:23 +0200)
framework/Db/lib/Horde/Db/Adapter.php
framework/Db/lib/Horde/Db/Adapter/Abstract.php [deleted file]
framework/Db/lib/Horde/Db/Adapter/Base.php [new file with mode: 0644]
framework/Db/lib/Horde/Db/Adapter/Oci8.php
framework/Db/lib/Horde/Db/Adapter/Pdo/Abstract.php [deleted file]
framework/Db/lib/Horde/Db/Adapter/Pdo/Base.php [new file with mode: 0644]
framework/Db/lib/Horde/Db/Adapter/Pdo/Mysql.php
framework/Db/lib/Horde/Db/Adapter/Pdo/Oci.php
framework/Db/lib/Horde/Db/Adapter/Pdo/Pgsql.php
framework/Db/lib/Horde/Db/Adapter/Pdo/Sqlite.php

index 74ab912..abb3fdd 100644 (file)
@@ -13,8 +13,6 @@
  */
 
 /**
- * Abstract parent class for Db Adapters.
- *
  * @author     Mike Naberezny <mike@maintainable.com>
  * @author     Derek DeVries <derek@maintainable.com>
  * @author     Chuck Hagenbuch <chuck@horde.org>
diff --git a/framework/Db/lib/Horde/Db/Adapter/Abstract.php b/framework/Db/lib/Horde/Db/Adapter/Abstract.php
deleted file mode 100644 (file)
index 9bcea39..0000000
+++ /dev/null
@@ -1,623 +0,0 @@
-<?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_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;
-        }
-
-        // 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_Abstract_' . $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 '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 ? 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) ? '' : " ($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";
-    }
-
-}
diff --git a/framework/Db/lib/Horde/Db/Adapter/Base.php b/framework/Db/lib/Horde/Db/Adapter/Base.php
new file mode 100644 (file)
index 0000000..bdd6f1e
--- /dev/null
@@ -0,0 +1,623 @@
+<?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_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;
+        }
+
+        // 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_Abstract_' . $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 '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 ? 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) ? '' : " ($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";
+    }
+
+}
index ce3da6b..74ad796 100644 (file)
@@ -69,7 +69,7 @@ every row returned J
  * @package    Horde_Db
  * @subpackage Adapter
  */
-class Horde_Db_Adapter_Oci8 extends Horde_Db_Adapter_Abstract
+class Horde_Db_Adapter_Oci8 extends Horde_Db_Adapter_Base
 {
     /**
      * @var string
diff --git a/framework/Db/lib/Horde/Db/Adapter/Pdo/Abstract.php b/framework/Db/lib/Horde/Db/Adapter/Pdo/Abstract.php
deleted file mode 100644 (file)
index 8855321..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-<?php
-/**
- * Copyright 2007 Maintainable Software, LLC
- * Copyright 2006-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_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:
-     *   selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3]
-     *
-     * @param   string  $sql
-     * @param   mixed   $arg1  Either an array of bound parameters or a query name.
-     * @param   string  $arg2  If $arg1 contains bound parameters, the query name.
-     */
-    public function selectValues($sql, $arg1=null, $arg2=null)
-    {
-        $result = $this->execute($sql, $arg1, $arg2);
-        return $result ? $result->fetchAll(PDO::FETCH_COLUMN, 0) : array();
-    }
-
-    /**
-     * Returns an array where the keys are the first column of a select, and the
-     * values are the second column:
-     *
-     *   selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler']
-     *
-     * @param   string  $sql
-     * @param   mixed   $arg1  Either an array of bound parameters or a query name.
-     * @param   string  $arg2  If $arg1 contains bound parameters, the query name.
-     */
-    public function selectAssoc($sql, $arg1=null, $arg2=null)
-    {
-        $result = $this->execute($sql, $arg1, $arg2);
-        return $result ? $result->fetchAll(PDO::FETCH_KEY_PAIR) : array();
-    }
-
-
-    /*##########################################################################
-    # Quoting
-    ##########################################################################*/
-
-    /**
-     * Quotes a string, escaping any ' (single quote) and \ (backslash)
-     * characters..
-     *
-     * @param   string  $string
-     * @return  string
-     */
-    public function quoteString($string)
-    {
-        return $this->_connection->quote($string);
-    }
-
-
-    /*##########################################################################
-    # Protected
-    ##########################################################################*/
-
-    protected function _checkRequiredConfig()
-    {
-        // 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'] = '';
-        }
-    }
-
-    protected function _normalizeConfig($params)
-    {
-        // normalize config parameters to what PDO expects
-        $normalize = array('database' => 'dbname', 'socket' => 'unix_socket', 'hostspec' => 'host');
-        foreach ($normalize as $from => $to) {
-            if (isset($params[$from])) {
-                $params[$to] = $params[$from];
-                unset($params[$from]);
-            }
-        }
-
-        return $params;
-    }
-
-    protected function _buildDsnString($params)
-    {
-        $dsn = $this->_config['adapter'] . ':';
-        foreach ($params as $k => $v) {
-            $dsn .= "$k=$v;";
-        }
-        return rtrim($dsn, ';');
-    }
-
-    /**
-     * Parse configuration array into options for PDO constructor.
-     *
-     * @throws  Horde_Db_Exception
-     * @return  array  [dsn, username, password]
-     */
-    protected function _parseConfig()
-    {
-        $this->_checkRequiredConfig();
-
-        // collect options to build PDO Data Source Name (DSN) string
-        $dsnOpts = $this->_config;
-        unset(
-            $dsnOpts['adapter'],
-            $dsnOpts['username'],
-            $dsnOpts['password'],
-            $dsnOpts['protocol'],
-            $dsnOpts['persistent'],
-            $dsnOpts['charset'],
-            $dsnOpts['phptype']
-        );
-
-        // return DSN and user/pass for connection
-        return array(
-            $this->_buildDsnString($this->_normalizeConfig($dsnOpts)),
-            $this->_config['username'],
-            $this->_config['password']);
-    }
-
-}
diff --git a/framework/Db/lib/Horde/Db/Adapter/Pdo/Base.php b/framework/Db/lib/Horde/Db/Adapter/Pdo/Base.php
new file mode 100644 (file)
index 0000000..b923301
--- /dev/null
@@ -0,0 +1,233 @@
+<?php
+/**
+ * Copyright 2007 Maintainable Software, LLC
+ * Copyright 2006-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_Pdo_Base extends Horde_Db_Adapter_Base
+{
+    /*##########################################################################
+    # 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:
+     *   selectValues("SELECT id FROM companies LIMIT 3") => [1,2,3]
+     *
+     * @param   string  $sql
+     * @param   mixed   $arg1  Either an array of bound parameters or a query name.
+     * @param   string  $arg2  If $arg1 contains bound parameters, the query name.
+     */
+    public function selectValues($sql, $arg1=null, $arg2=null)
+    {
+        $result = $this->execute($sql, $arg1, $arg2);
+        return $result ? $result->fetchAll(PDO::FETCH_COLUMN, 0) : array();
+    }
+
+    /**
+     * Returns an array where the keys are the first column of a select, and the
+     * values are the second column:
+     *
+     *   selectAssoc("SELECT id, name FROM companies LIMIT 3") => [1 => 'Ford', 2 => 'GM', 3 => 'Chrysler']
+     *
+     * @param   string  $sql
+     * @param   mixed   $arg1  Either an array of bound parameters or a query name.
+     * @param   string  $arg2  If $arg1 contains bound parameters, the query name.
+     */
+    public function selectAssoc($sql, $arg1=null, $arg2=null)
+    {
+        $result = $this->execute($sql, $arg1, $arg2);
+        return $result ? $result->fetchAll(PDO::FETCH_KEY_PAIR) : array();
+    }
+
+
+    /*##########################################################################
+    # Quoting
+    ##########################################################################*/
+
+    /**
+     * Quotes a string, escaping any ' (single quote) and \ (backslash)
+     * characters..
+     *
+     * @param   string  $string
+     * @return  string
+     */
+    public function quoteString($string)
+    {
+        return $this->_connection->quote($string);
+    }
+
+
+    /*##########################################################################
+    # Protected
+    ##########################################################################*/
+
+    protected function _checkRequiredConfig()
+    {
+        // 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'] = '';
+        }
+    }
+
+    protected function _normalizeConfig($params)
+    {
+        // normalize config parameters to what PDO expects
+        $normalize = array('database' => 'dbname', 'socket' => 'unix_socket', 'hostspec' => 'host');
+        foreach ($normalize as $from => $to) {
+            if (isset($params[$from])) {
+                $params[$to] = $params[$from];
+                unset($params[$from]);
+            }
+        }
+
+        return $params;
+    }
+
+    protected function _buildDsnString($params)
+    {
+        $dsn = $this->_config['adapter'] . ':';
+        foreach ($params as $k => $v) {
+            $dsn .= "$k=$v;";
+        }
+        return rtrim($dsn, ';');
+    }
+
+    /**
+     * Parse configuration array into options for PDO constructor.
+     *
+     * @throws  Horde_Db_Exception
+     * @return  array  [dsn, username, password]
+     */
+    protected function _parseConfig()
+    {
+        $this->_checkRequiredConfig();
+
+        // collect options to build PDO Data Source Name (DSN) string
+        $dsnOpts = $this->_config;
+        unset(
+            $dsnOpts['adapter'],
+            $dsnOpts['username'],
+            $dsnOpts['password'],
+            $dsnOpts['protocol'],
+            $dsnOpts['persistent'],
+            $dsnOpts['charset'],
+            $dsnOpts['phptype']
+        );
+
+        // return DSN and user/pass for connection
+        return array(
+            $this->_buildDsnString($this->_normalizeConfig($dsnOpts)),
+            $this->_config['username'],
+            $this->_config['password']);
+    }
+
+}
index b193411..522a6e1 100644 (file)
@@ -23,7 +23,7 @@
  * @package    Horde_Db
  * @subpackage Adapter
  */
-class Horde_Db_Adapter_Pdo_Mysql extends Horde_Db_Adapter_Pdo_Abstract
+class Horde_Db_Adapter_Pdo_Mysql extends Horde_Db_Adapter_Pdo_Base
 {
     /**
      * @var string
index ef72c1a..feb0561 100644 (file)
@@ -71,7 +71,7 @@ every row returned J
  * @package    Horde_Db
  * @subpackage Adapter
  */
-class Horde_Db_Adapter_Pdo_Oci extends Horde_Db_Adapter_Pdo_Abstract
+class Horde_Db_Adapter_Pdo_Oci extends Horde_Db_Adapter_Pdo_Base
 {
     /**
      * @var string
index 0889817..3092eb3 100644 (file)
@@ -23,7 +23,7 @@
  * @package    Horde_Db
  * @subpackage Adapter
  */
-class Horde_Db_Adapter_Pdo_Pgsql extends Horde_Db_Adapter_Pdo_Abstract
+class Horde_Db_Adapter_Pdo_Pgsql extends Horde_Db_Adapter_Pdo_Base
 {
     /**
      * @var string
index bae89f4..3c31b8c 100644 (file)
@@ -23,7 +23,7 @@
  * @package    Horde_Db
  * @subpackage Adapter
  */
-class Horde_Db_Adapter_Pdo_Sqlite extends Horde_Db_Adapter_Pdo_Abstract
+class Horde_Db_Adapter_Pdo_Sqlite extends Horde_Db_Adapter_Pdo_Base
 {
     /**
      * @var string